Unverified Commit 40bbac90 authored by Sergei Morozov's avatar Sergei Morozov

Merge branch '3.0.x'

parents 190a7afb 4cc12da9
......@@ -102,3 +102,87 @@ jobs:
- name: "Run squizlabs/php_codesniffer"
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
phpunit-oci8:
name: "PHPUnit on OCI8"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "7.4"
services:
oracle:
image: "wnameless/oracle-xe-11g-r2"
ports:
- "1521:1521"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
extensions: "oci8"
coverage: "pcov"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer install --no-interaction --no-progress --no-suggest"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit.oci8.xml --coverage-clover=coverage.xml"
- name: "Upload Code Coverage"
uses: "codecov/codecov-action@v1"
phpunit-pdo-oci:
name: "PHPUnit on PDO_OCI"
runs-on: "ubuntu-latest"
strategy:
matrix:
php-version:
- "7.4"
services:
oracle:
image: "wnameless/oracle-xe-11g-r2"
ports:
- "1521:1521"
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "${{ matrix.php-version }}"
extensions: "pdo_oci"
coverage: "pcov"
- name: "Cache dependencies installed with composer"
uses: "actions/cache@v1"
with:
path: "~/.composer/cache"
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
- name: "Install dependencies with composer"
run: "composer install --no-interaction --no-progress --no-suggest"
- name: "Run PHPUnit"
run: "vendor/bin/phpunit -c ci/github/phpunit.pdo-oci.xml --coverage-clover=coverage.xml"
- name: "Upload Code Coverage"
uses: "codecov/codecov-action@v1"
......@@ -17,16 +17,13 @@ Powerful database abstraction layer with many features for database schema intro
[Master image]: https://img.shields.io/travis/doctrine/dbal/master.svg?style=flat-square
[Coverage image]: https://codecov.io/gh/doctrine/dbal/branch/master/graph/badge.svg
[ContinuousPHP image]: https://img.shields.io/continuousphp/git-hub/doctrine/dbal/master.svg?style=flat-square
[Master]: https://travis-ci.org/doctrine/dbal
[CodeCov Master]: https://codecov.io/gh/doctrine/dbal/branch/master
[AppVeyor master]: https://ci.appveyor.com/project/doctrine/dbal/branch/master
[AppVeyor master image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/master?svg=true
[ContinuousPHP]: https://continuousphp.com/git-hub/doctrine/dbal
[2.10 image]: https://img.shields.io/travis/doctrine/dbal/2.10.x.svg?style=flat-square
[Coverage 2.10 image]: https://codecov.io/gh/doctrine/dbal/branch/2.10.x/graph/badge.svg
[ContinuousPHP 2.10 image]: https://img.shields.io/continuousphp/git-hub/doctrine/dbal/2.10.x.svg?style=flat-square
[2.10]: https://github.com/doctrine/dbal/tree/2.10.x
[CodeCov 2.10]: https://codecov.io/gh/doctrine/dbal/branch/2.10.x
[AppVeyor 2.10]: https://ci.appveyor.com/project/doctrine/dbal/branch/2.10.x
......
This diff is collapsed.
<?php
declare(strict_types=1);
use Doctrine\DBAL\DriverManager;
(static function (): void {
// workaround for https://bugs.php.net/bug.php?id=77120
DriverManager::getConnection([
'driver' => 'oci8',
'host' => 'oracle-xe-11',
'user' => 'ORACLE',
'password' => 'ORACLE',
'dbname' => 'XE',
])->query('ALTER USER ORACLE IDENTIFIED BY ORACLE');
})();
#!/bin/bash
set -euo pipefail
docker-php-ext-configure pdo_oci --with-pdo-oci=instantclient,/usr/local/instantclient
sudo -E env PHP_INI_DIR=/usr/local/etc/php docker-php-ext-install pdo_oci
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
bootstrap="bootstrap.php"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_driver" value="oci8"/>
<var name="db_host" value="oracle-xe-11" />
<var name="db_user" value="C##doctrine" />
<var name="db_password" value="ORACLE" />
<var name="db_dbname" value="XE" />
<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit"/>
<var name="tmpdb_driver" value="oci8"/>
<var name="tmpdb_host" value="oracle-xe-11" />
<var name="tmpdb_user" value="ORACLE" />
<var name="tmpdb_password" value="ORACLE" />
<var name="tmpdb_dbname" value="XE" />
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../src</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
bootstrap="bootstrap.php"
>
<php>
<var name="db_driver" value="pdo_oci"/>
<var name="db_host" value="oracle-xe-11" />
<var name="db_user" value="C##doctrine" />
<var name="db_password" value="ORACLE" />
<var name="db_dbname" value="XE" />
<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit"/>
<var name="tmpdb_driver" value="pdo_oci"/>
<var name="tmpdb_host" value="oracle-xe-11" />
<var name="tmpdb_user" value="ORACLE" />
<var name="tmpdb_password" value="ORACLE" />
<var name="tmpdb_dbname" value="XE" />
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../src</directory>
</whitelist>
</filter>
</phpunit>
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<var name="db_driver" value="oci8"/>
<var name="db_host" value="localhost"/>
<var name="db_user" value="doctrine"/>
<var name="db_password" value="oracle"/>
<var name="db_dbname" value="XE"/>
<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit"/>
<var name="tmpdb_driver" value="oci8"/>
<var name="tmpdb_host" value="localhost"/>
<var name="tmpdb_user" value="system"/>
<var name="tmpdb_password" value="oracle"/>
<var name="tmpdb_dbname" value="XE"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../lib</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
</exclude>
</groups>
</phpunit>
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
>
<php>
<var name="db_driver" value="pdo_oci"/>
<var name="db_host" value="localhost"/>
<var name="db_user" value="doctrine"/>
<var name="db_password" value="oracle"/>
<var name="db_dbname" value="XE"/>
<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit"/>
<var name="tmpdb_driver" value="pdo_oci"/>
<var name="tmpdb_host" value="localhost"/>
<var name="tmpdb_user" value="system"/>
<var name="tmpdb_password" value="oracle"/>
<var name="tmpdb_dbname" value="XE"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../../tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../lib</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
</exclude>
</groups>
</phpunit>
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f87aeb14c3190629c5d6b76523ea7bf1",
"content-hash": "acad7f4e4727e01178c00f5809ed760b",
"packages": [
{
"name": "composer/package-versions-deprecated",
......@@ -481,22 +481,22 @@
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.6.2",
"version": "v0.7.0",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a"
"reference": "e8d808670b8f882188368faaf1144448c169c0b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/8001af8eb107fbfcedc31a8b51e20b07d85b457a",
"reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7",
"reference": "e8d808670b8f882188368faaf1144448c169c0b7",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0",
"php": "^5.3|^7",
"squizlabs/php_codesniffer": "^2|^3"
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
"squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev"
},
"require-dev": {
"composer/composer": "*",
......@@ -543,26 +543,26 @@
"stylecheck",
"tests"
],
"time": "2020-01-29T20:22:20+00:00"
"time": "2020-06-25T14:57:39+00:00"
},
{
"name": "doctrine/coding-standard",
"version": "8.0.0",
"version": "8.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/coding-standard.git",
"reference": "742200e29fb8ffd58ba9abec8a9ec33db0884677"
"reference": "637003febec655f1b27f4301b44bf2264be57434"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/coding-standard/zipball/742200e29fb8ffd58ba9abec8a9ec33db0884677",
"reference": "742200e29fb8ffd58ba9abec8a9ec33db0884677",
"url": "https://api.github.com/repos/doctrine/coding-standard/zipball/637003febec655f1b27f4301b44bf2264be57434",
"reference": "637003febec655f1b27f4301b44bf2264be57434",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2",
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
"php": "^7.2 || ^8.0",
"slevomat/coding-standard": "^6.3.8",
"slevomat/coding-standard": "^6.3.9",
"squizlabs/php_codesniffer": "^3.5.5"
},
"type": "phpcodesniffer-standard",
......@@ -599,7 +599,7 @@
"standard",
"style"
],
"time": "2020-06-02T17:58:43+00:00"
"time": "2020-07-05T20:35:22+00:00"
},
{
"name": "doctrine/instantiator",
......@@ -1362,20 +1362,23 @@
},
{
"name": "phpstan/phpstan",
"version": "0.12.18",
"version": "0.12.33",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "1ce27fe29c8660a27926127d350d53d80c4d4286"
"reference": "46e698a0452526a05c4a351d7c47c4b8c37a548d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ce27fe29c8660a27926127d350d53d80c4d4286",
"reference": "1ce27fe29c8660a27926127d350d53d80c4d4286",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e698a0452526a05c4a351d7c47c4b8c37a548d",
"reference": "46e698a0452526a05c4a351d7c47c4b8c37a548d",
"shasum": ""
},
"require": {
"php": "^7.1"
"php": "^7.1|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
......@@ -1397,7 +1400,21 @@
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"time": "2020-03-22T16:51:47+00:00"
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://www.patreon.com/phpstan",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift"
}
],
"time": "2020-07-19T22:10:11+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
......@@ -2784,16 +2801,16 @@
},
{
"name": "slevomat/coding-standard",
"version": "6.3.8",
"version": "6.3.10",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
"reference": "500f55b5e2dee1dcef8e88f2038990acdba29f1c"
"reference": "58fa5ea2c048357ae55185eb5e93ca2826fffde0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/500f55b5e2dee1dcef8e88f2038990acdba29f1c",
"reference": "500f55b5e2dee1dcef8e88f2038990acdba29f1c",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/58fa5ea2c048357ae55185eb5e93ca2826fffde0",
"reference": "58fa5ea2c048357ae55185eb5e93ca2826fffde0",
"shasum": ""
},
"require": {
......@@ -2837,7 +2854,7 @@
"type": "tidelift"
}
],
"time": "2020-05-27T06:28:47+00:00"
"time": "2020-06-22T11:33:09+00:00"
},
{
"name": "squizlabs/php_codesniffer",
......
Architecture
============
As already said, the DBAL is a thin layer on top of PDO. PDO itself
is mainly defined in terms of 2 classes: ``PDO`` and
``PDOStatement``. The equivalent classes in the DBAL are
``Doctrine\DBAL\Connection`` and ``Doctrine\DBAL\Statement``. A
``Doctrine\DBAL\Connection`` wraps a
``Doctrine\DBAL\Driver\Connection`` and a
``Doctrine\DBAL\Statement`` wraps a
``Doctrine\DBAL\Driver\Statement``.
``Doctrine\DBAL\Driver\Connection`` and
``Doctrine\DBAL\Driver\Statement`` are just interfaces. These
interfaces are implemented by concrete drivers. For all PDO based
drivers, ``PDO`` and ``PDOStatement`` are the implementations of
these interfaces. Thus, for PDO-based drivers, a
``Doctrine\DBAL\Connection`` wraps a ``PDO`` instance and a
``Doctrine\DBAL\Statement`` wraps a ``PDOStatement`` instance. Even
more, a ``Doctrine\DBAL\Connection`` *is a*
``Doctrine\DBAL\Driver\Connection`` and a
``Doctrine\DBAL\Statement`` *is a*
``Doctrine\DBAL\Driver\Statement``.
What does a ``Doctrine\DBAL\Connection`` or a
``Doctrine\DBAL\Statement`` add to the underlying driver
The DBAL consists of two layers: drivers and a wrapper. Each layer
is mainly defined in terms of 3 components: ``Connection``,
``Statement`` and ``Result``.
A ``Doctrine\DBAL\Connection`` wraps a ``Doctrine\DBAL\Driver\Connection``,
a ``Doctrine\DBAL\Statement`` wraps a ``Doctrine\DBAL\Driver\Statement``
and a ``Doctrine\DBAL\Result`` wraps a ``Doctrine\DBAL\Driver\Result``.
``Doctrine\DBAL\Driver\Connection``, ``Doctrine\DBAL\Driver\Statement``
and ``Doctrine\DBAL\Driver\Result`` are just interfaces.
These interfaces are implemented by concrete drivers.
What do the wrapper components add to the underlying driver
implementations? The enhancements include SQL logging, events and
control over the transaction isolation level in a portable manner,
among others.
A DBAL driver is defined to the outside in terms of 3 interfaces:
``Doctrine\DBAL\Driver``, ``Doctrine\DBAL\Driver\Connection`` and
``Doctrine\DBAL\Driver\Statement``. The latter two resemble (a
subset of) the corresponding PDO API.
Apart from the three main components, a DBAL driver should also provide
an implementation of the ``Doctrine\DBAL\Driver`` interface that
has two primary purposes:
A concrete driver implementation must provide implementation
classes for these 3 interfaces.
1. Translate the DBAL connection parameters to the ones specific
to the driver's connection class.
2. Act as a factory of other driver-specific components like
platform, schema manager and exception converter.
The DBAL is separated into several different packages that
perfectly separate responsibilities of the different RDBMS layers.
separate responsibilities of the different RDBMS layers.
Drivers
-------
The drivers abstract a PHP specific database API by enforcing two
The drivers abstract a PHP specific database API by enforcing three
interfaces:
- ``\Doctrine\DBAL\Driver\Connection``
- ``\Doctrine\DBAL\Driver\Statement``
The above two interfaces require exactly the same methods as PDO.
- ``\Doctrine\DBAL\Driver\Result``
Platforms
---------
......@@ -59,8 +48,8 @@ features a platform supports. The
denominator of what a database platform has to publish to the
userland, to be fully supportable by Doctrine. This includes the
SchemaTool, Transaction Isolation and many other features. The
Database platform for MySQL for example can be used by all 3 MySQL
extensions, PDO, Mysqli and ext/mysql.
Database platform for MySQL for example can be used by multiple
MySQL extensions: pdo_mysql and mysqli.
Logging
-------
......
......@@ -142,7 +142,7 @@ use prepared statements:
SQL query, bind the given params with their binding types and execute the query.
This method returns the executed prepared statement for iteration and is useful
for SELECT statements.
- ``executeUpdate($sql, $params, $types)`` - Create a prepared statement for the passed
- ``executeStatement($sql, $params, $types)`` - Create a prepared statement for the passed
SQL query, bind the given params with their binding types and execute the query.
This method returns the number of affected rows by the executed query and is useful
for UPDATE, DELETE and INSERT statements.
......@@ -170,7 +170,7 @@ of this query using the fetch API of a statement:
The fetch API of a prepared statement obviously works only for ``SELECT`` queries.
If you find it tedious to write all the prepared statement code you can alternatively use
the ``Doctrine\DBAL\Connection#executeQuery()`` and ``Doctrine\DBAL\Connection#executeUpdate()``
the ``Doctrine\DBAL\Connection#executeQuery()`` and ``Doctrine\DBAL\Connection#executeStatement()``
methods. See the API section below on details how to use them.
Additionally there are lots of convenience methods for data-retrieval and manipulation
......@@ -208,7 +208,7 @@ which means this code works independent of the database you are using.
.. note::
Be aware this type conversion only works with ``Statement#bindValue()``,
``Connection#executeQuery()`` and ``Connection#executeUpdate()``. It
``Connection#executeQuery()`` and ``Connection#executeStatement()``. It
is not supported to pass a doctrine type name to ``Statement#bindParam()``,
because this would not work with binding by reference.
......@@ -286,7 +286,7 @@ This is much more complicated and is ugly to write generically.
.. note::
The parameter list support only works with ``Doctrine\DBAL\Connection::executeQuery()``
and ``Doctrine\DBAL\Connection::executeUpdate()``, NOT with the binding methods of
and ``Doctrine\DBAL\Connection::executeStatement()``, NOT with the binding methods of
a prepared statement.
API
......@@ -319,7 +319,7 @@ Prepare a given SQL statement and return the
)
*/
executeUpdate()
executeStatement()
~~~~~~~~~~~~~~~
Executes a prepared statement with the given SQL and parameters and
......@@ -328,7 +328,7 @@ returns the affected rows count:
.. code-block:: php
<?php
$count = $conn->executeUpdate('UPDATE user SET username = ? WHERE id = ?', array('jwage', 1));
$count = $conn->executeStatement('UPDATE user SET username = ? WHERE id = ?', array('jwage', 1));
echo $count; // 1
The ``$types`` variable contains the PDO or Doctrine Type constants
......
......@@ -135,7 +135,7 @@ are using just the DBAL there are also helper methods which simplify the usage q
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $connection->executeQuery($sql, array($_GET['username']));
There is also ``executeUpdate`` which does not return a statement but the number of affected rows.
There is also ``executeStatement`` which does not return a statement but the number of affected rows.
Besides binding parameters you can also pass the type of the variable. This allows Doctrine or the underlying
vendor to not only escape but also cast the value to the correct type. See the docs on querying and DQL in the
......
......@@ -89,8 +89,8 @@
phpcs wrongly complains about them, and that has been reported here:
https://github.com/squizlabs/PHP_CodeSniffer/issues/2950
-->
<exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern>
<exclude-pattern>src/Driver/Mysqli/MysqliConnection.php</exclude-pattern>
<exclude-pattern>src/Driver/IBMDB2/Connection.php</exclude-pattern>
<exclude-pattern>src/Driver/Mysqli/Exception/ConnectionFailed.php</exclude-pattern>
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<exclude-pattern>src/SQLParserUtils.php</exclude-pattern>
<exclude-pattern>src/Tools/Dumper.php</exclude-pattern>
......@@ -104,9 +104,10 @@
<exclude-pattern>tests/Functional/ResultCacheTest.php</exclude-pattern>
</rule>
<!-- See https://github.com/slevomat/coding-standard/issues/1038 -->
<!-- See https://github.com/slevomat/coding-standard/issues/770 -->
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
<exclude-pattern>tests/Functional/Driver/IBMDB2/DB2StatementTest.php</exclude-pattern>
<exclude-pattern>src/Driver/ExceptionConverterDriver.php</exclude-pattern>
<exclude-pattern>src/Query/QueryBuilder.php</exclude-pattern>
</rule>
<!-- see https://github.com/doctrine/dbal/issues/3377 -->
......@@ -114,15 +115,9 @@
<exclude-pattern>src/Schema/Comparator.php</exclude-pattern>
</rule>
<!-- DB2_AUTOCOMMIT_ON/DB2_AUTOCOMMIT_OFF are of int type but db2_autocommit() incorrectly expects bool,
see https://bugs.php.net/bug.php?id=77591 -->
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing">
<exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern>
</rule>
<!-- The SQLSRV_* functions are defined in the upper case by the sqlsrv extension and violate the standard
see https://docs.microsoft.com/en-us/sql/connect/php/constants-microsoft-drivers-for-php-for-sql-server -->
<rule ref="Squiz.PHP.LowercasePHPFunctions">
<exclude-pattern>src/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern>
<exclude-pattern>src/Driver/SQLSrv/Statement.php</exclude-pattern>
</rule>
</ruleset>
......@@ -3,19 +3,13 @@ parameters:
paths:
- %currentWorkingDirectory%/src
- %currentWorkingDirectory%/tests
autoload_files:
scanFiles:
- %currentWorkingDirectory%/tests/phpstan-polyfill.php
treatPhpDocTypesAsCertain: false
reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false
ignoreErrors:
# extension not available
- '~^(Used )?(Function|Constant) sasql_\S+ not found\.\z~i'
# https://bugs.php.net/bug.php?id=78126
- '~^Call to an undefined method PDO::sqliteCreateFunction\(\)\.\z~'
# legacy remnants from doctrine/common
- '~^Class Doctrine\\Common\\(Collections\\Collection|Persistence\\Proxy) not found\.\z~'
- '~^.+ on an unknown class Doctrine\\Common\\(Collections\\Collection|Persistence\\Proxy)\.\z~'
......@@ -26,24 +20,6 @@ parameters:
# weird class name, represented in stubs as OCI_(Lob|Collection)
- '~unknown class OCI-(Lob|Collection)~'
# https://github.com/doctrine/dbal/pull/3582/files#r290847062
-
message: '~Parameter #3 \$type of method Doctrine\\DBAL\\Driver\\Statement::bindValue\(\) expects int, string given\.~'
path: %currentWorkingDirectory%/tests/Functional/DataAccessTest.php
# https://github.com/JetBrains/phpstorm-stubs/pull/766
-
message: '~^Strict comparison using === between true and null will always evaluate to false\.$~'
path: %currentWorkingDirectory%/src/Driver/Mysqli/Result.php
# The ReflectionException in the case when the class does not exist is acceptable and does not need to be handled
- '~^Parameter #1 \$argument of class ReflectionClass constructor expects class-string<T of object>\|T of object, string given\.$~'
# https://github.com/phpstan/phpstan/issues/3132
-
message: '~^Call to function in_array\(\) with arguments Doctrine\\DBAL\\Schema\\Column, array<string> and true will always evaluate to false\.$~'
path: %currentWorkingDirectory%/src/Schema/Table.php
# https://github.com/phpstan/phpstan/issues/3133
-
message: '~^Cannot cast array<string>\|bool\|string\|null to int\.$~'
......@@ -52,19 +28,12 @@ parameters:
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/553
-
message: '~^Call to function assert\(\) with true will always evaluate to true\.$~'
path: %currentWorkingDirectory%/src/Driver/PDOConnection.php
path: %currentWorkingDirectory%/src/Driver/PDO/Connection.php
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/553
-
message: '~^Strict comparison using !== between int and false will always evaluate to true\.$~'
path: %currentWorkingDirectory%/src/Driver/PDOConnection.php
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/732
-
message: '~^Access to undefined constant PDO::PGSQL_ATTR_DISABLE_PREPARES\.$~'
paths:
- %currentWorkingDirectory%/src/Driver/PDOPgSql/Driver.php
- %currentWorkingDirectory%/tests/Driver/PDOPgSql/DriverTest.php
path: %currentWorkingDirectory%/src/Driver/PDO/Connection.php
# False Positive
- '~Strict comparison using === between 1 and 2 will always evaluate to false~'
......@@ -81,7 +50,6 @@ parameters:
-
message: '~^Construct empty\(\) is not allowed. Use more strict comparison\.~'
paths:
- %currentWorkingDirectory%/src/Driver/*/*Connection.php
- %currentWorkingDirectory%/src/Driver/*/Driver.php
- %currentWorkingDirectory%/src/Driver/AbstractOracleDriver/EasyConnectString.php
- %currentWorkingDirectory%/src/Platforms/*Platform.php
......@@ -110,27 +78,16 @@ parameters:
# Temporaily suppressed during up-merging an upgrade PHPStan 0.12
- '~^Unable to resolve the template type ExpectedType in call to method static method PHPUnit\\Framework\\Assert::assertInstanceOf\(\)$~'
- '~^Unable to resolve the template type RealInstanceType in call to method PHPUnit\\Framework\\TestCase::getMockClass\(\)$~'
# Temporaily suppressed during up-merging an upgrade PHPStan 0.12
-
message: '~^Parameter #1 \$expected of static method PHPUnit\\Framework\\Assert::assertInstanceOf\(\) expects class-string<object>, string given\.$~'
path: %currentWorkingDirectory%/tests/Driver/AbstractDriverTest.php
# Temporaily suppressed during up-merging an upgrade PHPStan 0.12
-
message: '~^Call to an undefined method Doctrine\\DBAL\\Driver::createDatabasePlatformForVersion\(\)\.$~'
path: %currentWorkingDirectory%/tests/Driver/AbstractDriverTest.php
# This is not reproducible on PHPStan 0.12.30
-
message: '~^Method Doctrine\\DBAL\\Tests\\Functional\\Driver\\Mysqli\\ConnectionTest::getConnection\(\) should return Doctrine\\DBAL\\Driver\\Mysqli\\MysqliConnection but returns Doctrine\\DBAL\\Driver\\Connection\.$~'
path: %currentWorkingDirectory%/tests/Functional/Driver/Mysqli/ConnectionTest.php
# This is not reproducible on PHPStan 0.12.30
# Temporaily suppressed during up-merging an upgrade to PHPStan 0.12.33
-
message: '~^Method Doctrine\\DBAL\\Tests\\Functional\\Driver\\PDOSqlsrv\\DriverTest::getConnection\(\) should return Doctrine\\DBAL\\Driver\\PDOConnection but returns Doctrine\\DBAL\\Driver\\Connection\.$~'
path: %currentWorkingDirectory%/tests/Functional/Driver/PDOSqlsrv/DriverTest.php
message: '~^Parameter #1 \$expected of static method PHPUnit\\Framework\\Assert::assertInstanceOf\(\) expects class-string<Doctrine\\DBAL\\Connection&PHPUnit\\Framework\\MockObject\\MockObject>, class-string<Doctrine\\DBAL\\Connection>&class-string<PHPUnit\\Framework\\MockObject\\MockObject> given\.$~'
path: %currentWorkingDirectory%/tests/DriverManagerTest.php
# Caused by phpdoc annotations intended for Psalm
-
......@@ -141,9 +98,7 @@ parameters:
- %currentWorkingDirectory%/tests/ConnectionTest.php
- %currentWorkingDirectory%/tests/DriverManagerTest.php
- %currentWorkingDirectory%/tests/Functional/ConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/PDOPgsqlConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/SQLAnywhere/ConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/SQLAnywhere/StatementTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/PDO/PgSQL/ConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/ExceptionTest.php
- %currentWorkingDirectory%/tests/Functional/PortabilityTest.php
- %currentWorkingDirectory%/tests/Functional/PrimaryReadReplicaConnectionTest.php
......@@ -151,15 +106,6 @@ parameters:
- %currentWorkingDirectory%/tests/Schema/Synchronizer/SingleDatabaseSynchronizerTest.php
- %currentWorkingDirectory%/tests/TestUtil.php
-
message: '~Method Doctrine\\DBAL\\Driver\\PDOSqlsrv\\Connection\:\:lastInsertId\(\) should return string but returns string\|false\|null\.~'
paths:
- %currentWorkingDirectory%/src/Driver/PDOSqlsrv/Connection.php
-
message: '~Return type \(Doctrine\\DBAL\\Portability\\Statement\) of method Doctrine\\DBAL\\Portability\\Connection::prepare\(\) should be compatible with return type \(Doctrine\\DBAL\\Statement\) of method Doctrine\\DBAL\\Connection::prepare\(\)~'
paths:
- %currentWorkingDirectory%/src/Portability/Connection.php
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
......
......@@ -37,8 +37,8 @@
<!--
Fixing these issues requires an API change
-->
<file name="src/Driver/PDOSqlsrv/Connection.php"/>
<file name="src/Driver/SQLSrv/SQLSrvConnection.php"/>
<file name="src/Driver/PDO/SQLSrv/Connection.php"/>
<file name="src/Driver/SQLSrv/Connection.php"/>
</errorLevel>
</FalsableReturnStatement>
<ImpureMethodCall>
......@@ -64,7 +64,7 @@
Requires a release of
https://github.com/JetBrains/phpstorm-stubs/pull/727
-->
<file name="src/Driver/SQLSrv/SQLSrvStatement.php"/>
<file name="src/Driver/SQLSrv/Statement.php"/>
</errorLevel>
</TooFewArguments>
<UndefinedConstant>
......@@ -73,7 +73,7 @@
Requires a release of
https://github.com/JetBrains/phpstorm-stubs/pull/732
-->
<file name="tests/Driver/PDOPgSql/DriverTest.php" />
<file name="tests/Driver/PDO/PgSQL/DriverTest.php" />
</errorLevel>
</UndefinedConstant>
<UndefinedClass>
......
......@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Result;
......@@ -130,7 +130,7 @@ final class CachingResult implements Result
/**
* @return array<string,mixed>|false
*
* @throws DriverException
* @throws Exception
*/
private function fetch()
{
......
......@@ -65,9 +65,9 @@ class QueryCacheProfile
*
* @return string[]
*/
public function generateCacheKeys(string $query, array $params, array $types, array $connectionParams = []): array
public function generateCacheKeys(string $sql, array $params, array $types, array $connectionParams = []): array
{
$realCacheKey = 'query=' . $query .
$realCacheKey = 'query=' . $sql .
'&params=' . serialize($params) .
'&types=' . serialize($types) .
'&connectionParams=' . hash('sha256', serialize($connectionParams));
......
......@@ -11,15 +11,11 @@ final class ColumnCase
{
/**
* Convert column names to upper case.
*
* @see \PDO::CASE_UPPER
*/
public const UPPER = 1;
/**
* Convert column names to lower case.
*
* @see \PDO::CASE_LOWER
*/
public const LOWER = 2;
......
......@@ -5,17 +5,20 @@ declare(strict_types=1);
namespace Doctrine\DBAL;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Logging\NullLogger;
use Doctrine\DBAL\Logging\SQLLogger;
/**
* Configuration container for the Doctrine DBAL.
*
* @internal When adding a new configuration option just write a getter/setter
* pair and add the option to the _attributes array with a proper default value.
* @internal
*/
class Configuration
{
/** @var Middleware[] */
private $middlewares = [];
/**
* The attributes that are contained in the configuration.
* Values are default values.
......@@ -101,4 +104,24 @@ class Configuration
{
return $this->_attributes['autoCommit'] ?? true;
}
/**
* @param Middleware[] $middlewares
*
* @return $this
*/
public function setMiddlewares(array $middlewares): self
{
$this->middlewares = $middlewares;
return $this;
}
/**
* @return Middleware[]
*/
public function getMiddlewares(): array
{
return $this->middlewares;
}
}
This diff is collapsed.
......@@ -10,17 +10,14 @@ use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\Exception as DriverException;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Statement;
use InvalidArgumentException;
use function array_rand;
use function assert;
use function count;
use function func_get_args;
/**
* Primary-Replica Connection
......@@ -32,9 +29,8 @@ use function func_get_args;
*
* 1. Replica if primary was never picked before and ONLY if 'getWrappedConnection'
* or 'executeQuery' is used.
* 2. Primary picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint',
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or
* 'prepare' is called.
* 2. Primary picked when 'executeStatement', 'insert', 'delete', 'update', 'createSavepoint',
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit' or 'prepare' is called.
* 3. If Primary was picked once during the lifetime of the connection it will always get picked afterwards.
* 4. One replica connection is randomly picked ONCE during a request.
*
......@@ -47,7 +43,7 @@ use function func_get_args;
* Be aware that Connection#executeQuery is a method specifically for READ
* operations only.
*
* Use Connection#executeUpdate for any SQL statement that changes/updates
* Use Connection#executeStatement for any SQL statement that changes/updates
* state in the database (UPDATE, INSERT, DELETE or DDL statements).
*
* This connection is limited to replica operations using the
......@@ -97,8 +93,11 @@ class PrimaryReadReplicaConnection extends Connection
/**
* Creates Primary Replica Connection.
*
* @internal The connection can be only instantiated by the driver manager.
*
* @param array<string, mixed> $params
*
* @throws DBALException
* @throws InvalidArgumentException
*/
public function __construct(
......@@ -218,6 +217,8 @@ class PrimaryReadReplicaConnection extends Connection
/**
* Connects to a specific connection.
*
* @throws DBALException
*/
protected function connectTo(string $connectionName): DriverConnection
{
......@@ -228,7 +229,7 @@ class PrimaryReadReplicaConnection extends Connection
try {
return $this->_driver->connect($connectionParams);
} catch (DriverException $e) {
throw DBALException::driverException($this->_driver, $e);
throw $this->convertException($e);
}
}
......@@ -255,11 +256,11 @@ class PrimaryReadReplicaConnection extends Connection
/**
* {@inheritDoc}
*/
public function executeUpdate(string $query, array $params = [], array $types = []): int
public function executeStatement($sql, array $params = [], array $types = []): int
{
$this->ensureConnectedToPrimary();
return parent::executeUpdate($query, $params, $types);
return parent::executeStatement($sql, $params, $types);
}
public function beginTransaction(): void
......@@ -283,16 +284,6 @@ class PrimaryReadReplicaConnection extends Connection
parent::rollBack();
}
/**
* {@inheritDoc}
*/
public function delete(string $table, array $identifier, array $types = []): int
{
$this->ensureConnectedToPrimary();
return parent::delete($table, $identifier, $types);
}
public function close(): void
{
unset($this->connections['primary'], $this->connections['replica']);
......@@ -303,33 +294,6 @@ class PrimaryReadReplicaConnection extends Connection
$this->connections = ['primary' => null, 'replica' => null];
}
/**
* {@inheritDoc}
*/
public function update(string $table, array $data, array $identifier, array $types = []): int
{
$this->ensureConnectedToPrimary();
return parent::update($table, $data, $identifier, $types);
}
/**
* {@inheritDoc}
*/
public function insert(string $table, array $data, array $types = []): int
{
$this->ensureConnectedToPrimary();
return parent::insert($table, $data, $types);
}
public function exec(string $statement): int
{
$this->ensureConnectedToPrimary();
return parent::exec($statement);
}
public function createSavepoint(string $savepoint): void
{
$this->ensureConnectedToPrimary();
......@@ -351,23 +315,6 @@ class PrimaryReadReplicaConnection extends Connection
parent::rollbackSavepoint($savepoint);
}
public function query(string $sql): Result
{
$this->ensureConnectedToPrimary();
assert($this->_conn instanceof DriverConnection);
$args = func_get_args();
$logger = $this->getConfiguration()->getSQLLogger();
$logger->startQuery($sql);
$statement = $this->_conn->query($sql);
$logger->stopQuery();
return $statement;
}
public function prepare(string $sql): Statement
{
$this->ensureConnectedToPrimary();
......
......@@ -4,86 +4,11 @@ declare(strict_types=1);
namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
use Doctrine\DBAL\Exception\DriverException;
use Exception;
use Throwable;
use function array_map;
use function bin2hex;
use function implode;
use function is_resource;
use function is_string;
use function json_encode;
use function preg_replace;
use function sprintf;
/**
* @psalm-immutable
*/
class DBALException extends Exception
{
/**
* @param mixed[] $params
*/
public static function driverExceptionDuringQuery(Driver $driver, Throwable $driverEx, string $sql, array $params = []): self
{
$messageFormat = <<<'MESSAGE'
An exception occurred while executing "%s"%s:
%s
MESSAGE;
$message = sprintf(
$messageFormat,
$sql,
$params !== [] ? sprintf(' with params %s', self::formatParameters($params)) : '',
$driverEx->getMessage()
);
return static::wrapException($driver, $driverEx, $message);
}
public static function driverException(Driver $driver, Throwable $driverEx): self
{
return static::wrapException($driver, $driverEx, sprintf('An exception occurred in driver with message: %s', $driverEx->getMessage()));
}
private static function wrapException(Driver $driver, Throwable $driverEx, string $msg): self
{
if ($driverEx instanceof DriverException) {
return $driverEx;
}
if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverExceptionInterface) {
return $driver->convertException($msg, $driverEx);
}
return new self($msg, 0, $driverEx);
}
/**
* Returns a human-readable representation of an array of parameters.
* This properly handles binary data by returning a hex representation.
*
* @param array<mixed, mixed> $params
*/
private static function formatParameters(array $params): string
{
return '[' . implode(', ', array_map(static function ($param): string {
if (is_resource($param)) {
return (string) $param;
}
$json = @json_encode($param);
if (! is_string($json) || $json === 'null' && is_string($param)) {
// JSON encoding failed, this is not a UTF-8 string.
return sprintf('"%s"', preg_replace('/.{2}/', '\\x$0', bin2hex($param)));
}
return $json;
}, $params)) . ']';
}
}
......@@ -4,8 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
......@@ -22,7 +23,7 @@ interface Driver
*
* @return DriverConnection The database connection.
*
* @throws DriverException
* @throws Exception
*/
public function connect(array $params): DriverConnection;
......@@ -38,5 +39,10 @@ interface Driver
* Gets the SchemaManager that can be used to inspect and change the underlying
* database schema of the platform this driver connects to.
*/
public function getSchemaManager(Connection $conn): AbstractSchemaManager;
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager;
/**
* Gets the ExceptionConverter that can be used to convert driver-level exceptions into DBAL exceptions.
*/
public function getExceptionConverter(): ExceptionConverter;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
interface ExceptionConverter
{
/**
* Converts a given driver-level exception into a DBAL-level driver exception.
*
* Implementors should use the vendor-specific error code and SQLSTATE of the exception
* and instantiate the most appropriate specialized {@link DriverException} subclass.
*
* @param string $message The exception message to use.
* @param Exception $exception The driver exception to convert.
*
* @return DriverException An instance of {@link DriverException} or one of its subclasses.
*/
public function convert(string $message, Exception $exception): DriverException;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\IBMDB2;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
final class ExceptionConverter implements ExceptionConverterInterface
{
public function convert(string $message, Exception $exception): DriverException
{
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link https://dev.mysql.com/doc/refman/8.0/en/client-error-reference.html
* @link https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html
*/
public function convert(string $message, Exception $exception): DriverException
{
switch ($exception->getCode()) {
case 1213:
return new DeadlockException($message, $exception);
case 1205:
return new LockWaitTimeoutException($message, $exception);
case 1050:
return new TableExistsException($message, $exception);
case 1051:
case 1146:
return new TableNotFoundException($message, $exception);
case 1216:
case 1217:
case 1451:
case 1452:
case 1701:
return new ForeignKeyConstraintViolationException($message, $exception);
case 1062:
case 1557:
case 1569:
case 1586:
return new UniqueConstraintViolationException($message, $exception);
case 1054:
case 1166:
case 1611:
return new InvalidFieldNameException($message, $exception);
case 1052:
case 1060:
case 1110:
return new NonUniqueFieldNameException($message, $exception);
case 1064:
case 1149:
case 1287:
case 1341:
case 1342:
case 1343:
case 1344:
case 1382:
case 1479:
case 1541:
case 1554:
case 1626:
return new SyntaxErrorException($message, $exception);
case 1044:
case 1045:
case 1046:
case 1049:
case 1095:
case 1142:
case 1143:
case 1227:
case 1370:
case 1429:
case 2002:
case 2005:
return new ConnectionException($message, $exception);
case 2006:
return new ConnectionLost($message, $exception);
case 1048:
case 1121:
case 1138:
case 1171:
case 1252:
case 1263:
case 1364:
case 1566:
return new NotNullConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\OCI;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link http://www.dba-oracle.com/t_error_code_list.htm
*/
public function convert(string $message, Exception $exception): DriverException
{
switch ($exception->getCode()) {
case 1:
case 2299:
case 38911:
return new UniqueConstraintViolationException($message, $exception);
case 904:
return new InvalidFieldNameException($message, $exception);
case 918:
case 960:
return new NonUniqueFieldNameException($message, $exception);
case 923:
return new SyntaxErrorException($message, $exception);
case 942:
return new TableNotFoundException($message, $exception);
case 955:
return new TableExistsException($message, $exception);
case 1017:
case 12545:
return new ConnectionException($message, $exception);
case 1400:
return new NotNullConstraintViolationException($message, $exception);
case 2266:
case 2291:
case 2292:
return new ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\PostgreSQL;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use function strpos;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
*/
public function convert(string $message, Exception $exception): DriverException
{
switch ($exception->getSQLState()) {
case '40001':
case '40P01':
return new DeadlockException($message, $exception);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
if (strpos($exception->getMessage(), 'truncate') !== false) {
return new ForeignKeyConstraintViolationException($message, $exception);
}
break;
case '23502':
return new NotNullConstraintViolationException($message, $exception);
case '23503':
return new ForeignKeyConstraintViolationException($message, $exception);
case '23505':
return new UniqueConstraintViolationException($message, $exception);
case '42601':
return new SyntaxErrorException($message, $exception);
case '42702':
return new NonUniqueFieldNameException($message, $exception);
case '42703':
return new InvalidFieldNameException($message, $exception);
case '42P01':
return new TableNotFoundException($message, $exception);
case '42P07':
return new TableExistsException($message, $exception);
}
// In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code.
// The exception code is always set to 7 here.
// We have to match against the SQLSTATE in the error message in these cases.
if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) {
return new ConnectionException($message, $exception);
}
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\SQLSrv;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\DriverException;
final class ExceptionConverter implements ExceptionConverterInterface
{
public function convert(string $message, Exception $exception): DriverException
{
return new DriverException($message, $exception);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\API\SQLite;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\ReadOnlyException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use function strpos;
final class ExceptionConverter implements ExceptionConverterInterface
{
/**
* @link http://www.sqlite.org/c3ref/c_abort.html
*/
public function convert(string $message, Exception $exception): DriverException
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new LockWaitTimeoutException($message, $exception);
}
if (
strpos($exception->getMessage(), 'must be unique') !== false ||
strpos($exception->getMessage(), 'is not unique') !== false ||
strpos($exception->getMessage(), 'are not unique') !== false ||
strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false
) {
return new UniqueConstraintViolationException($message, $exception);
}
if (
strpos($exception->getMessage(), 'may not be NULL') !== false ||
strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false
) {
return new NotNullConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'no such table:') !== false) {
return new TableNotFoundException($message, $exception);
}
if (strpos($exception->getMessage(), 'already exists') !== false) {
return new TableExistsException($message, $exception);
}
if (strpos($exception->getMessage(), 'has no column named') !== false) {
return new InvalidFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'ambiguous column name') !== false) {
return new NonUniqueFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'syntax error') !== false) {
return new SyntaxErrorException($message, $exception);
}
if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) {
return new ReadOnlyException($message, $exception);
}
if (strpos($exception->getMessage(), 'unable to open database file') !== false) {
return new ConnectionException($message, $exception);
}
if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) {
return new ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
}
......@@ -6,13 +6,15 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\API\IBMDB2\ExceptionConverter;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\DB2SchemaManager;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for IBM DB2 based drivers.
* Abstract base implementation of the {@link Driver} interface for IBM DB2 based drivers.
*/
abstract class AbstractDB2Driver implements Driver
{
......@@ -21,8 +23,13 @@ abstract class AbstractDB2Driver implements Driver
return new DB2Platform();
}
public function getSchemaManager(Connection $conn): AbstractSchemaManager
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return new DB2SchemaManager($conn);
return new DB2SchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverterInterface
{
return new ExceptionConverter();
}
}
......@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Exception;
use Exception as BaseException;
use Throwable;
/**
......@@ -12,7 +12,7 @@ use Throwable;
*
* @psalm-immutable
*/
abstract class AbstractDriverException extends Exception implements DriverException
abstract class AbstractException extends BaseException implements Exception
{
/**
* The SQLSTATE of the driver.
......
......@@ -6,9 +6,8 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
......@@ -24,157 +23,10 @@ use function stripos;
use function version_compare;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for MySQL based drivers.
* Abstract base implementation of the {@link Driver} interface for MySQL based drivers.
*/
abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionAwarePlatformDriver
abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
{
/**#@+
* MySQL server error codes.
*
* @link https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html
*/
private const ER_DBACCESS_DENIED_ERROR = 1044;
private const ER_ACCESS_DENIED_ERROR = 1045;
private const ER_NO_DB_ERROR = 1046;
private const ER_BAD_NULL_ERROR = 1048;
private const ER_BAD_DB_ERROR = 1049;
private const ER_TABLE_EXISTS_ERROR = 1050;
private const ER_BAD_TABLE_ERROR = 1051;
private const ER_NON_UNIQ_ERROR = 1052;
private const ER_BAD_FIELD_ERROR = 1054;
private const ER_DUP_FIELDNAME = 1060;
private const ER_DUP_ENTRY = 1062;
private const ER_PARSE_ERROR = 1064;
private const ER_KILL_DENIED_ERROR = 1095;
private const ER_FIELD_SPECIFIED_TWICE = 1110;
private const ER_NULL_COLUMN_IN_INDEX = 1121;
private const ER_INVALID_USE_OF_NULL = 1138;
private const ER_TABLEACCESS_DENIED_ERROR = 1142;
private const ER_COLUMNACCESS_DENIED_ERROR = 1143;
private const ER_NO_SUCH_TABLE = 1146;
private const ER_SYNTAX_ERROR = 1149;
private const ER_WRONG_COLUMN_NAME = 1166;
private const ER_PRIMARY_CANT_HAVE_NULL = 1171;
private const ER_LOCK_WAIT_TIMEOUT = 1205;
private const ER_LOCK_DEADLOCK = 1213;
private const ER_NO_REFERENCED_ROW = 1216;
private const ER_ROW_IS_REFERENCED = 1217;
private const ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227;
private const ER_SPATIAL_CANT_HAVE_NULL = 1252;
private const ER_WARN_NULL_TO_NOTNULL = 1263;
private const ER_WARN_DEPRECATED_SYNTAX = 1287;
private const ER_FPARSER_BAD_HEADER = 1341;
private const ER_FPARSER_EOF_IN_COMMENT = 1342;
private const ER_FPARSER_ERROR_IN_PARAMETER = 1343;
private const ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344;
private const ER_NO_DEFAULT_FOR_FIELD = 1364;
private const ER_PROCACCESS_DENIED_ERROR = 1370;
private const ER_RESERVED_SYNTAX = 1382;
private const ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429;
private const ER_ROW_IS_REFERENCED_2 = 1451;
private const ER_NO_REFERENCED_ROW_2 = 1452;
private const ER_PARTITION_REQUIRES_VALUES_ERROR = 1479;
private const ER_EVENT_DROP_FAILED = 1541;
private const ER_WARN_DEPRECATED_SYNTAX_WITH_VER = 1554;
private const ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED = 1557;
private const ER_NULL_IN_VALUES_LESS_THAN = 1566;
private const ER_DUP_ENTRY_AUTOINCREMENT_CASE = 1569;
private const ER_DUP_ENTRY_WITH_KEY_NAME = 1586;
private const ER_LOAD_DATA_INVALID_COLUMN = 1611;
private const ER_CONFLICT_FN_PARSE_ERROR = 1626;
private const ER_TRUNCATE_ILLEGAL_FK = 1701;
/**#@-*/
/**#@+
* MySQL client error codes.
*
* @link https://dev.mysql.com/doc/refman/8.0/en/client-error-reference.html
*/
private const CR_CONNECTION_ERROR = 2002;
private const CR_UNKNOWN_HOST = 2005;
/**#@-*/
public function convertException(string $message, DriverExceptionInterface $exception): DriverException
{
switch ($exception->getCode()) {
case self::ER_LOCK_DEADLOCK:
return new Exception\DeadlockException($message, $exception);
case self::ER_LOCK_WAIT_TIMEOUT:
return new Exception\LockWaitTimeoutException($message, $exception);
case self::ER_TABLE_EXISTS_ERROR:
return new Exception\TableExistsException($message, $exception);
case self::ER_BAD_TABLE_ERROR:
case self::ER_NO_SUCH_TABLE:
return new Exception\TableNotFoundException($message, $exception);
case self::ER_NO_REFERENCED_ROW:
case self::ER_ROW_IS_REFERENCED:
case self::ER_ROW_IS_REFERENCED_2:
case self::ER_NO_REFERENCED_ROW_2:
case self::ER_TRUNCATE_ILLEGAL_FK:
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
case self::ER_DUP_ENTRY:
case self::ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED:
case self::ER_DUP_ENTRY_AUTOINCREMENT_CASE:
case self::ER_DUP_ENTRY_WITH_KEY_NAME:
return new Exception\UniqueConstraintViolationException($message, $exception);
case self::ER_BAD_FIELD_ERROR:
case self::ER_WRONG_COLUMN_NAME:
case self::ER_LOAD_DATA_INVALID_COLUMN:
return new Exception\InvalidFieldNameException($message, $exception);
case self::ER_NON_UNIQ_ERROR:
case self::ER_DUP_FIELDNAME:
case self::ER_FIELD_SPECIFIED_TWICE:
return new Exception\NonUniqueFieldNameException($message, $exception);
case self::ER_PARSE_ERROR:
case self::ER_SYNTAX_ERROR:
case self::ER_WARN_DEPRECATED_SYNTAX:
case self::ER_FPARSER_BAD_HEADER:
case self::ER_FPARSER_EOF_IN_COMMENT:
case self::ER_FPARSER_ERROR_IN_PARAMETER:
case self::ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER:
case self::ER_RESERVED_SYNTAX:
case self::ER_PARTITION_REQUIRES_VALUES_ERROR:
case self::ER_EVENT_DROP_FAILED:
case self::ER_WARN_DEPRECATED_SYNTAX_WITH_VER:
case self::ER_CONFLICT_FN_PARSE_ERROR:
return new Exception\SyntaxErrorException($message, $exception);
case self::ER_DBACCESS_DENIED_ERROR:
case self::ER_ACCESS_DENIED_ERROR:
case self::ER_NO_DB_ERROR:
case self::ER_BAD_DB_ERROR:
case self::ER_KILL_DENIED_ERROR:
case self::ER_TABLEACCESS_DENIED_ERROR:
case self::ER_COLUMNACCESS_DENIED_ERROR:
case self::ER_SPECIFIC_ACCESS_DENIED_ERROR:
case self::ER_PROCACCESS_DENIED_ERROR:
case self::ER_CONNECT_TO_FOREIGN_DATA_SOURCE:
case self::CR_CONNECTION_ERROR:
case self::CR_UNKNOWN_HOST:
return new Exception\ConnectionException($message, $exception);
case self::ER_BAD_NULL_ERROR:
case self::ER_NULL_COLUMN_IN_INDEX:
case self::ER_INVALID_USE_OF_NULL:
case self::ER_PRIMARY_CANT_HAVE_NULL:
case self::ER_SPATIAL_CANT_HAVE_NULL:
case self::ER_WARN_NULL_TO_NOTNULL:
case self::ER_NO_DEFAULT_FOR_FIELD:
case self::ER_NULL_IN_VALUES_LESS_THAN:
return new Exception\NotNullConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
/**
* {@inheritdoc}
*
......@@ -276,8 +128,13 @@ abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionA
*
* @return MySqlSchemaManager
*/
public function getSchemaManager(Connection $conn): AbstractSchemaManager
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return new MySqlSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverter
{
return new MySqlSchemaManager($conn);
return new MySQL\ExceptionConverter();
}
}
......@@ -7,67 +7,31 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\OCI;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\OracleSchemaManager;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Oracle based drivers.
* Abstract base implementation of the {@link Driver} interface for Oracle based drivers.
*/
abstract class AbstractOracleDriver implements Driver, ExceptionConverterDriver
abstract class AbstractOracleDriver implements Driver
{
public function convertException(string $message, DriverExceptionInterface $exception): DriverException
public function getDatabasePlatform(): AbstractPlatform
{
switch ($exception->getCode()) {
case 1:
case 2299:
case 38911:
return new Exception\UniqueConstraintViolationException($message, $exception);
case 904:
return new Exception\InvalidFieldNameException($message, $exception);
case 918:
case 960:
return new Exception\NonUniqueFieldNameException($message, $exception);
case 923:
return new Exception\SyntaxErrorException($message, $exception);
case 942:
return new Exception\TableNotFoundException($message, $exception);
case 955:
return new Exception\TableExistsException($message, $exception);
case 1017:
case 12545:
return new Exception\ConnectionException($message, $exception);
case 1400:
return new Exception\NotNullConstraintViolationException($message, $exception);
case 2266:
case 2291:
case 2292:
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
return new OraclePlatform();
}
public function getDatabasePlatform(): AbstractPlatform
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return new OraclePlatform();
return new OracleSchemaManager($conn, $platform);
}
public function getSchemaManager(Connection $conn): AbstractSchemaManager
public function getExceptionConverter(): ExceptionConverter
{
return new OracleSchemaManager($conn);
return new OCI\ExceptionConverter();
}
/**
......
......@@ -5,9 +5,8 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\PostgreSQL;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion;
use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
......@@ -17,70 +16,13 @@ use Doctrine\DBAL\Schema\PostgreSqlSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use function preg_match;
use function strpos;
use function version_compare;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for PostgreSQL based drivers.
* Abstract base implementation of the {@link Driver} interface for PostgreSQL based drivers.
*/
abstract class AbstractPostgreSQLDriver implements ExceptionConverterDriver, VersionAwarePlatformDriver
abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @link http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
*/
public function convertException(string $message, DriverExceptionInterface $exception): DriverException
{
switch ($exception->getSQLState()) {
case '40001':
case '40P01':
return new Exception\DeadlockException($message, $exception);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
if (strpos($exception->getMessage(), 'truncate') !== false) {
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
}
break;
case '23502':
return new Exception\NotNullConstraintViolationException($message, $exception);
case '23503':
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
case '23505':
return new Exception\UniqueConstraintViolationException($message, $exception);
case '42601':
return new Exception\SyntaxErrorException($message, $exception);
case '42702':
return new Exception\NonUniqueFieldNameException($message, $exception);
case '42703':
return new Exception\InvalidFieldNameException($message, $exception);
case '42P01':
return new Exception\TableNotFoundException($message, $exception);
case '42P07':
return new Exception\TableExistsException($message, $exception);
}
// In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code.
// The exception code is always set to 7 here.
// We have to match against the SQLSTATE in the error message in these cases.
if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) {
return new Exception\ConnectionException($message, $exception);
}
return new DriverException($message, $exception);
}
public function createDatabasePlatformForVersion(string $version): AbstractPlatform
{
if (preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts) === 0) {
......@@ -107,8 +49,13 @@ abstract class AbstractPostgreSQLDriver implements ExceptionConverterDriver, Ver
return new PostgreSQL94Platform();
}
public function getSchemaManager(Connection $conn): AbstractSchemaManager
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return new PostgreSqlSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverter
{
return new PostgreSqlSchemaManager($conn);
return new PostgreSQL\ExceptionConverter();
}
}
......@@ -6,13 +6,15 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\API\ExceptionConverter as ExceptionConverterInterface;
use Doctrine\DBAL\Driver\API\SQLSrv\ExceptionConverter;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\SQLServer2012Platform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SQLServerSchemaManager;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Microsoft SQL Server based drivers.
* Abstract base implementation of the {@link Driver} interface for Microsoft SQL Server based drivers.
*/
abstract class AbstractSQLServerDriver implements Driver
{
......@@ -21,8 +23,13 @@ abstract class AbstractSQLServerDriver implements Driver
return new SQLServer2012Platform();
}
public function getSchemaManager(Connection $conn): AbstractSchemaManager
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return new SQLServerSchemaManager($conn);
return new SQLServerSchemaManager($conn, $platform);
}
public function getExceptionConverter(): ExceptionConverterInterface
{
return new ExceptionConverter();
}
}
......@@ -2,16 +2,16 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\AbstractSQLServerDriver;
namespace Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception;
use Doctrine\DBAL\Driver\AbstractDriverException;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class PortWithoutHost extends AbstractDriverException
final class PortWithoutHost extends AbstractException
{
public static function new(): self
{
......
......@@ -6,90 +6,30 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\API\SQLite;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SqliteSchemaManager;
use function strpos;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers.
* Abstract base implementation of the {@link Driver} interface for SQLite based drivers.
*/
abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver
abstract class AbstractSQLiteDriver implements Driver
{
/**
* {@inheritdoc}
*
* @link http://www.sqlite.org/c3ref/c_abort.html
*/
public function convertException(string $message, DriverExceptionInterface $exception): DriverException
public function getDatabasePlatform(): AbstractPlatform
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new Exception\LockWaitTimeoutException($message, $exception);
}
if (
strpos($exception->getMessage(), 'must be unique') !== false ||
strpos($exception->getMessage(), 'is not unique') !== false ||
strpos($exception->getMessage(), 'are not unique') !== false ||
strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false
) {
return new Exception\UniqueConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) {
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
}
if (
strpos($exception->getMessage(), 'may not be NULL') !== false ||
strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false
) {
return new Exception\NotNullConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'no such table:') !== false) {
return new Exception\TableNotFoundException($message, $exception);
}
if (strpos($exception->getMessage(), 'already exists') !== false) {
return new Exception\TableExistsException($message, $exception);
}
if (strpos($exception->getMessage(), 'has no column named') !== false) {
return new Exception\InvalidFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'ambiguous column name') !== false) {
return new Exception\NonUniqueFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'syntax error') !== false) {
return new Exception\SyntaxErrorException($message, $exception);
}
if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) {
return new Exception\ReadOnlyException($message, $exception);
}
if (strpos($exception->getMessage(), 'unable to open database file') !== false) {
return new Exception\ConnectionException($message, $exception);
}
return new DriverException($message, $exception);
return new SqlitePlatform();
}
public function getDatabasePlatform(): AbstractPlatform
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return new SqlitePlatform();
return new SqliteSchemaManager($conn, $platform);
}
public function getSchemaManager(Connection $conn): AbstractSchemaManager
public function getExceptionConverter(): ExceptionConverter
{
return new SqliteSchemaManager($conn);
return new SQLite\ExceptionConverter();
}
}
......@@ -4,25 +4,23 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\DBALException;
/**
* Connection interface.
* Driver connections must implement this interface.
*
* This resembles (a subset of) the PDO interface.
*/
interface Connection
{
/**
* Prepares a statement for execution and returns a Statement object.
*
* @throws Exception
*/
public function prepare(string $sql): Statement;
/**
* Executes an SQL statement, returning a result set as a Statement object.
*
* @throws DBALException
* @throws Exception
*/
public function query(string $sql): Result;
......@@ -34,33 +32,35 @@ interface Connection
/**
* Executes an SQL statement and return the number of affected rows.
*
* @throws DBALException
* @throws Exception
*/
public function exec(string $statement): int;
public function exec(string $sql): int;
/**
* Returns the ID of the last inserted row or sequence value.
*
* @throws Exception
*/
public function lastInsertId(?string $name = null): string;
/**
* Initiates a transaction.
*
* @throws DriverException
* @throws Exception
*/
public function beginTransaction(): void;
/**
* Commits a transaction.
*
* @throws DriverException
* @throws Exception
*/
public function commit(): void;
/**
* Rolls back the current transaction, as initiated by beginTransaction().
*
* @throws DriverException
* @throws Exception
*/
public function rollBack(): void;
}
......@@ -14,7 +14,7 @@ use Throwable;
*
* @psalm-immutable
*/
interface DriverException extends Throwable
interface Exception extends Throwable
{
/**
* Returns the SQLSTATE the driver was in at the time the error occurred.
......
......@@ -2,16 +2,16 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
namespace Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\OCI8\OCI8Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class IdentityColumnsNotSupported extends OCI8Exception
final class IdentityColumnsNotSupported extends AbstractException
{
public static function new(): self
{
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\DBALException;
use function sprintf;
/**
* @psalm-immutable
*/
final class UnknownFetchMode extends DBALException
{
public static function new(int $fetchMode): self
{
return new self(sprintf('Unknown fetch mode %d.', $fetchMode));
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\DBALException;
use function sprintf;
/**
* @psalm-immutable
*/
final class UnknownParamType extends DBALException
{
public static function new(int $type): self
{
return new self(sprintf('Unknown param type %d.', $type));
}
}
......@@ -2,22 +2,24 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
namespace Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class UnknownType extends MysqliException
final class UnknownParameterType extends AbstractException
{
/**
* @param mixed $type
*/
public static function new($type): self
{
return new self(sprintf('Unknown type, %d given.', $type));
return new self(sprintf('Unknown parameter type, %d given.', $type));
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Exception\DriverException;
/**
* Contract for a driver that is capable of converting DBAL driver exceptions into standardized DBAL driver exceptions.
*/
interface ExceptionConverterDriver
{
/**
* Converts a given DBAL driver exception into a standardized DBAL driver exception.
*
* It evaluates the vendor specific error code and SQLSTATE and transforms
* it into a unified {@link Doctrine\DBAL\Exception\DriverException} subclass.
*
* @param string $message The DBAL exception message to use.
* @param DriverExceptionInterface $exception The DBAL driver exception to convert.
*
* @return DriverException An instance of one of the DriverException subclasses.
*/
public function convertException(string $message, DriverExceptionInterface $exception): DriverException;
}
......@@ -12,7 +12,7 @@ final class FetchUtils
/**
* @return mixed|false
*
* @throws DriverException
* @throws Exception
*/
public static function fetchOne(Result $result)
{
......@@ -28,7 +28,7 @@ final class FetchUtils
/**
* @return array<int,array<int,mixed>>
*
* @throws DriverException
* @throws Exception
*/
public static function fetchAllNumeric(Result $result): array
{
......@@ -44,7 +44,7 @@ final class FetchUtils
/**
* @return array<int,array<string,mixed>>
*
* @throws DriverException
* @throws Exception
*/
public static function fetchAllAssociative(Result $result): array
{
......@@ -60,7 +60,7 @@ final class FetchUtils
/**
* @return array<int,mixed>
*
* @throws DriverException
* @throws Exception
*/
public static function fetchFirstColumn(Result $result): array
{
......
<?php
declare(strict_types=0);
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
......@@ -29,15 +30,17 @@ use function error_get_last;
use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON;
final class DB2Connection implements ServerInfoAwareConnection
final class Connection implements ServerInfoAwareConnection
{
/** @var resource */
private $conn;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param array<string,mixed> $driverOptions
*
* @throws DB2Exception
* @throws Exception
*/
public function __construct(
string $database,
......@@ -75,7 +78,7 @@ final class DB2Connection implements ServerInfoAwareConnection
throw PrepareFailed::new(error_get_last()['message']);
}
return new DB2Statement($stmt);
return new Statement($stmt);
}
public function query(string $sql): ResultInterface
......@@ -88,9 +91,9 @@ final class DB2Connection implements ServerInfoAwareConnection
return "'" . db2_escape_string($input) . "'";
}
public function exec(string $statement): int
public function exec(string $sql): int
{
$stmt = @db2_exec($this->conn, $statement);
$stmt = @db2_exec($this->conn, $sql);
if ($stmt === false) {
throw ConnectionError::new($this->conn);
......
......@@ -5,19 +5,16 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\AbstractDB2Driver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
/**
* IBM DB2 Driver.
*/
final class DB2Driver extends AbstractDB2Driver
final class Driver extends AbstractDB2Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params): Connection
public function connect(array $params): ConnectionInterface
{
return new DB2Connection(
return new Connection(
DataSourceName::fromConnectionParameters($params)->toString(),
isset($params['persistent']) && $params['persistent'] === true,
$params['user'] ?? '',
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotCopyStreamToStream extends AbstractException
{
public static function new(string $message): self
{
return new self('Could not copy source stream to temporary file: ' . $message);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotCreateTemporaryFile extends AbstractException
{
public static function new(string $message): self
{
return new self('Could not create temporary file: ' . $message);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotWriteToTemporaryFile extends AbstractException
{
public static function new(string $message): self
{
return new self('Could not write string to temporary file: ' . $message);
}
}
......@@ -4,15 +4,17 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionError extends DB2Exception
final class ConnectionError extends AbstractException
{
/**
* @param resource $connection
......
......@@ -4,15 +4,17 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionFailed extends DB2Exception
final class ConnectionFailed extends AbstractException
{
public static function new(): self
{
......
......@@ -4,12 +4,14 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class PrepareFailed extends DB2Exception
final class PrepareFailed extends AbstractException
{
public static function new(string $message): self
{
......
......@@ -4,15 +4,17 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_stmt_error;
use function db2_stmt_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class StatementError extends DB2Exception
final class StatementError extends AbstractException
{
/**
* @param resource $statement
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use function db2_fetch_array;
......@@ -13,7 +14,6 @@ use function db2_free_result;
use function db2_num_fields;
use function db2_num_rows;
use function db2_stmt_error;
use function db2_stmt_errormsg;
final class Result implements ResultInterface
{
......@@ -21,6 +21,8 @@ final class Result implements ResultInterface
private $statement;
/**
* @internal The result can be only instantiated by its driver connection or statement.
*
* @param resource $statement
*/
public function __construct($statement)
......@@ -36,7 +38,7 @@ final class Result implements ResultInterface
$row = @db2_fetch_array($this->statement);
if ($row === false && db2_stmt_error($this->statement) !== '02000') {
throw new DB2Exception(db2_stmt_errormsg($this->statement));
throw StatementError::new($this->statement);
}
return $row;
......@@ -50,7 +52,7 @@ final class Result implements ResultInterface
$row = @db2_fetch_assoc($this->statement);
if ($row === false && db2_stmt_error($this->statement) !== '02000') {
throw new DB2Exception(db2_stmt_errormsg($this->statement));
throw StatementError::new($this->statement);
}
return $row;
......
......@@ -4,9 +4,13 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCopyStreamToStream;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCreateTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotWriteToTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
......@@ -28,7 +32,7 @@ use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
final class DB2Statement implements Statement
final class Statement implements StatementInterface
{
/** @var resource */
private $stmt;
......@@ -45,6 +49,8 @@ final class DB2Statement implements Statement
private $lobs = [];
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $stmt
*/
public function __construct($stmt)
......@@ -98,7 +104,7 @@ final class DB2Statement implements Statement
* @param int $position Parameter position
* @param mixed $variable
*
* @throws DB2Exception
* @throws Exception
*/
private function bind(int $position, &$variable, int $parameterType, int $dataType): void
{
......@@ -152,14 +158,14 @@ final class DB2Statement implements Statement
/**
* @return resource
*
* @throws DB2Exception
* @throws Exception
*/
private function createTemporaryFile()
{
$handle = @tmpfile();
if ($handle === false) {
throw new DB2Exception('Could not create temporary file: ' . error_get_last()['message']);
throw CannotCreateTemporaryFile::new(error_get_last()['message']);
}
return $handle;
......@@ -169,24 +175,24 @@ final class DB2Statement implements Statement
* @param resource $source
* @param resource $target
*
* @throws DB2Exception
* @throws Exception
*/
private function copyStreamToStream($source, $target): void
{
if (@stream_copy_to_stream($source, $target) === false) {
throw new DB2Exception('Could not copy source stream to temporary file: ' . error_get_last()['message']);
throw CannotCopyStreamToStream::new(error_get_last()['message']);
}
}
/**
* @param resource $target
*
* @throws DB2Exception
* @throws Exception
*/
private function writeStringToStream(string $string, $target): void
{
if (@fwrite($target, $string) === false) {
throw new DB2Exception('Could not write string to temporary file: ' . error_get_last()['message']);
throw CannotWriteToTemporaryFile::new(error_get_last()['message']);
}
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver;
interface Middleware
{
public function wrap(Driver $driver): Driver;
}
......@@ -4,8 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
use Doctrine\DBAL\Driver\PingableConnection;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
......@@ -15,7 +16,7 @@ use function floor;
use function mysqli_init;
use function stripos;
class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
final class Connection implements ServerInfoAwareConnection
{
/**
* Name of the option to set connection flags
......@@ -26,10 +27,12 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
private $conn;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param iterable<Initializer> $preInitializers
* @param iterable<Initializer> $postInitializers
*
* @throws MysqliException
* @throws Exception
*/
public function __construct(
string $host = '',
......@@ -49,11 +52,7 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
}
if (! @$connection->real_connect($host, $username, $password, $database, $port, $socket, $flags)) {
throw new MysqliException(
$connection->connect_error,
'HY000',
$connection->connect_errno
);
throw ConnectionFailed::new($connection);
}
foreach ($postInitializers as $initializer) {
......@@ -97,7 +96,7 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
public function prepare(string $sql): DriverStatement
{
return new MysqliStatement($this->conn, $sql);
return new Statement($this->conn, $sql);
}
public function query(string $sql): ResultInterface
......@@ -110,9 +109,9 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
return "'" . $this->conn->escape_string($input) . "'";
}
public function exec(string $statement): int
public function exec(string $sql): int
{
if ($this->conn->query($statement) === false) {
if ($this->conn->query($sql) === false) {
throw ConnectionError::new($this->conn);
}
......@@ -142,16 +141,4 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
throw ConnectionError::new($this->conn);
}
}
/**
* Pings the server and re-connects when `mysqli.reconnect = 1`
*
* {@inheritDoc}
*/
public function ping(): void
{
if (! $this->conn->ping()) {
throw new MysqliException($this->conn->error, $this->conn->sqlstate, $this->conn->errno);
}
}
}
......@@ -5,7 +5,8 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\Mysqli\Exception\HostRequired;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Charset;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Options;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure;
......@@ -17,9 +18,9 @@ final class Driver extends AbstractMySQLDriver
/**
* {@inheritdoc}
*
* @return MysqliConnection
* @return Connection
*/
public function connect(array $params): Connection
public function connect(array $params): ConnectionInterface
{
if (! empty($params['persistent'])) {
if (! isset($params['host'])) {
......@@ -38,9 +39,9 @@ final class Driver extends AbstractMySQLDriver
if (isset($params['driver_options'])) {
$driverOptions = $params['driver_options'];
if (isset($driverOptions[MysqliConnection::OPTION_FLAGS])) {
$flags = $driverOptions[MysqliConnection::OPTION_FLAGS];
unset($driverOptions[MysqliConnection::OPTION_FLAGS]);
if (isset($driverOptions[Connection::OPTION_FLAGS])) {
$flags = $driverOptions[Connection::OPTION_FLAGS];
unset($driverOptions[Connection::OPTION_FLAGS]);
}
$preInitializers = $this->withOptions($preInitializers, $driverOptions);
......@@ -49,7 +50,7 @@ final class Driver extends AbstractMySQLDriver
$preInitializers = $this->withSecure($preInitializers, $params);
$postInitializers = $this->withCharset($postInitializers, $params);
return new MysqliConnection(
return new Connection(
$host,
$params['user'] ?? '',
$params['password'] ?? '',
......
......@@ -4,23 +4,18 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionError extends MysqliException
final class ConnectionError extends AbstractException
{
public static function new(mysqli $connection): self
{
$connectionSQLState = $connection->sqlstate;
$sqlState = null;
if ($connectionSQLState !== false) {
$sqlState = $connectionSQLState;
}
return new self($connection->error, $sqlState, $connection->errno);
return new self($connection->error, $connection->sqlstate, $connection->errno);
}
}
......@@ -4,17 +4,18 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use function sprintf;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
/**
* @internal
*
* @psalm-immutable
*/
final class UnknownFetchMode extends MysqliException
final class ConnectionFailed extends AbstractException
{
public static function new(int $fetchMode): self
public static function new(mysqli $connection): self
{
return new self(sprintf('Unknown fetch mode %d.', $fetchMode));
return new self($connection->connect_error, 'HY000', $connection->connect_errno);
}
}
......@@ -4,17 +4,19 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class FailedReadingStreamOffset extends MysqliException
final class FailedReadingStreamOffset extends AbstractException
{
public static function new(int $offset): self
public static function new(int $parameter): self
{
return new self(sprintf('Failed reading the stream resource for parameter offset %d.', $offset));
return new self(sprintf('Failed reading the stream resource for parameter #%d.', $parameter));
}
}
......@@ -2,14 +2,16 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class HostRequired extends MysqliException
final class HostRequired extends AbstractException
{
public static function forPersistentConnection(): self
{
......
......@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
use function sprintf;
......@@ -14,7 +14,7 @@ use function sprintf;
*
* @psalm-immutable
*/
final class InvalidCharset extends MysqliException
final class InvalidCharset extends AbstractException
{
public static function fromCharset(mysqli $connection, string $charset): self
{
......
......@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
......@@ -13,7 +13,7 @@ use function sprintf;
*
* @psalm-immutable
*/
final class InvalidOption extends MysqliException
final class InvalidOption extends AbstractException
{
/**
* @param mixed $value
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class NonStreamResourceUsedAsLargeObject extends AbstractException
{
public static function new(int $parameter): self
{
return new self(
sprintf('The resource passed as a LARGE_OBJECT parameter #%d must be of type "stream"', $parameter)
);
}
}
......@@ -4,13 +4,15 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use Doctrine\DBAL\Driver\AbstractException;
use mysqli_stmt;
/**
* @internal
*
* @psalm-immutable
*/
final class StatementError extends MysqliException
final class StatementError extends AbstractException
{
public static function new(mysqli_stmt $statement): self
{
......
......@@ -4,12 +4,13 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use mysqli;
interface Initializer
{
/**
* @throws MysqliException
* @throws Exception
*/
public function initialize(mysqli $connection): void;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* Exception thrown in case the mysqli driver errors.
*
* @psalm-immutable
*/
class MysqliException extends AbstractDriverException
{
}
......@@ -4,7 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use mysqli_stmt;
use stdClass;
......@@ -41,7 +43,9 @@ final class Result implements ResultInterface
private $boundValues = [];
/**
* @throws MysqliException
* @internal The result can be only instantiated by its driver connection or statement.
*
* @throws Exception
*/
public function __construct(mysqli_stmt $statement)
{
......@@ -71,7 +75,7 @@ final class Result implements ResultInterface
// Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
// it will have to allocate as much memory as it may be needed for the given column type
// (e.g. for a LONGBLOB field it's 4 gigabytes)
// (e.g. for a LONGBLOB column it's 4 gigabytes)
// @link https://bugs.php.net/bug.php?id=51386#1270673122
//
// Make sure that the values are bound after each execution. Otherwise, if free() has been
......@@ -88,7 +92,7 @@ final class Result implements ResultInterface
}
if (! $this->statement->bind_result(...$refs)) {
throw new MysqliException($this->statement->error, $this->statement->sqlstate, $this->statement->errno);
throw StatementError::new($this->statement);
}
}
......@@ -100,7 +104,7 @@ final class Result implements ResultInterface
$ret = $this->statement->fetch();
if ($ret === false) {
throw new MysqliException($this->statement->error, $this->statement->sqlstate, $this->statement->errno);
throw StatementError::new($this->statement);
}
if ($ret === null) {
......
......@@ -4,14 +4,14 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Exception\UnknownParameterType;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset;
use Doctrine\DBAL\Driver\Mysqli\Exception\NonStreamResourceUsedAsLargeObject;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownType;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use mysqli;
use mysqli_stmt;
......@@ -26,7 +26,7 @@ use function is_int;
use function is_resource;
use function str_repeat;
final class MysqliStatement implements Statement
final class Statement implements StatementInterface
{
/** @var string[] */
private static $paramTypeMap = [
......@@ -58,7 +58,9 @@ final class MysqliStatement implements Statement
private $values = [];
/**
* @throws MysqliException
* @internal The statement can be only instantiated by its driver connection.
*
* @throws Exception
*/
public function __construct(mysqli $conn, string $sql)
{
......@@ -89,7 +91,7 @@ final class MysqliStatement implements Statement
assert(is_int($param));
if (! isset(self::$paramTypeMap[$type])) {
throw UnknownType::new($type);
throw UnknownParameterType::new($type);
}
$this->boundValues[$param] =& $variable;
......@@ -104,7 +106,7 @@ final class MysqliStatement implements Statement
assert(is_int($param));
if (! isset(self::$paramTypeMap[$type])) {
throw UnknownType::new($type);
throw UnknownParameterType::new($type);
}
$this->values[$param] = $value;
......@@ -135,7 +137,7 @@ final class MysqliStatement implements Statement
/**
* Binds parameters with known types previously bound to the statement
*
* @throws DriverException
* @throws Exception
*/
private function bindTypedParameters(): void
{
......@@ -151,7 +153,7 @@ final class MysqliStatement implements Statement
if ($types[$parameter - 1] === self::$paramTypeMap[ParameterType::LARGE_OBJECT]) {
if (is_resource($value)) {
if (get_resource_type($value) !== 'stream') {
throw new InvalidArgumentException('Resources passed with the LARGE_OBJECT parameter type must be stream resources.');
throw NonStreamResourceUsedAsLargeObject::new($parameter);
}
$streams[$parameter] = $value;
......@@ -177,7 +179,7 @@ final class MysqliStatement implements Statement
*
* @param array<int, resource> $streams
*
* @throws MysqliException
* @throws Exception
*/
private function sendLongData(array $streams): void
{
......
......@@ -4,29 +4,29 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\OCI8\Exception\IdentityColumnsNotSupported;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Exception\IdentityColumnsNotSupported;
use Doctrine\DBAL\Driver\OCI8\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\OCI8\Exception\Error;
use Doctrine\DBAL\Driver\OCI8\Exception\SequenceDoesNotExist;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use UnexpectedValueException;
use function addcslashes;
use function assert;
use function oci_commit;
use function oci_connect;
use function oci_error;
use function oci_pconnect;
use function oci_rollback;
use function oci_server_version;
use function preg_match;
use function sprintf;
use function str_replace;
use const OCI_NO_AUTO_COMMIT;
/**
* OCI8 implementation of the Connection interface.
*/
final class OCI8Connection implements Connection, ServerInfoAwareConnection
final class Connection implements ConnectionInterface, ServerInfoAwareConnection
{
/** @var resource */
private $connection;
......@@ -37,7 +37,9 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
/**
* Creates a Connection to an Oracle Database using oci8 extension.
*
* @throws OCI8Exception
* @internal The connection can be only instantiated by its driver.
*
* @throws Exception
*/
public function __construct(
string $username,
......@@ -54,43 +56,30 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
}
if ($connection === false) {
throw OCI8Exception::fromErrorInfo(oci_error());
throw ConnectionFailed::new();
}
$this->connection = $connection;
$this->executionMode = new ExecutionMode();
$this->executionMode = new ExecutionMode();
}
/**
* {@inheritdoc}
*
* @throws UnexpectedValueException If the version string returned by the database server
* does not contain a parsable version number.
*/
public function getServerVersion(): string
{
$version = oci_server_version($this->connection);
if ($version === false) {
throw OCI8Exception::fromErrorInfo(oci_error($this->connection));
throw Error::new($this->connection);
}
if (preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches) === 0) {
throw new UnexpectedValueException(
sprintf(
'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' .
'Please report this database version string to the Doctrine team.',
$version
)
);
}
assert(preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches) === 1);
return $matches[1];
}
public function prepare(string $sql): DriverStatement
{
return new OCI8Statement($this->connection, $sql, $this->executionMode);
return new Statement($this->connection, $sql, $this->executionMode);
}
public function query(string $sql): ResultInterface
......@@ -103,9 +92,9 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
return "'" . addcslashes(str_replace("'", "''", $input), "\000\n\r\\\032") . "'";
}
public function exec(string $statement): int
public function exec(string $sql): int
{
return $this->prepare($statement)->execute()->rowCount();
return $this->prepare($sql)->execute()->rowCount();
}
public function lastInsertId(?string $name = null): string
......@@ -117,7 +106,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
$result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne();
if ($result === false) {
throw new OCI8Exception('lastInsertId failed: Query was executed but no result was returned.');
throw SequenceDoesNotExist::new();
}
return $result;
......@@ -131,7 +120,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
public function commit(): void
{
if (! oci_commit($this->connection)) {
throw OCI8Exception::fromErrorInfo(oci_error($this->connection));
throw Error::new($this->connection);
}
$this->executionMode->enableAutoCommit();
......@@ -140,7 +129,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
public function rollBack(): void
{
if (! oci_rollback($this->connection)) {
throw OCI8Exception::fromErrorInfo(oci_error($this->connection));
throw Error::new($this->connection);
}
$this->executionMode->enableAutoCommit();
......
......@@ -4,6 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\OCI8\Exception\NonTerminatedStringLiteral;
use function count;
use function implode;
use function preg_match;
......@@ -29,7 +32,7 @@ final class ConvertPositionalToNamedPlaceholders
*
* @return mixed[] [0] => the statement value (string), [1] => the paramMap value (array).
*
* @throws OCI8Exception
* @throws Exception
*/
public function __invoke(string $statement): array
{
......@@ -52,7 +55,7 @@ final class ConvertPositionalToNamedPlaceholders
}
} while ($result);
if ($currentLiteralDelimiter) {
if ($currentLiteralDelimiter !== null) {
throw NonTerminatedStringLiteral::new($tokenOffset - 1);
}
......
......@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use const OCI_NO_AUTO_COMMIT;
......@@ -17,9 +17,9 @@ final class Driver extends AbstractOracleDriver
/**
* {@inheritdoc}
*/
public function connect(array $params): Connection
public function connect(array $params): ConnectionInterface
{
return new OCI8Connection(
return new Connection(
$params['user'] ?? '',
$params['password'] ?? '',
$this->constructDsn($params),
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function assert;
use function oci_error;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionFailed extends AbstractException
{
public static function new(): self
{
$error = oci_error();
assert($error !== false);
return new self($error['message'], null, $error['code']);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function assert;
use function oci_error;
/**
* @internal
*
* @psalm-immutable
*/
final class Error extends AbstractException
{
/**
* @param resource $resource
*/
public static function new($resource): self
{
$error = oci_error($resource);
assert($error !== false);
return new self($error['message'], null, $error['code']);
}
}
......@@ -2,14 +2,18 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class NonTerminatedStringLiteral extends OCI8Exception
final class NonTerminatedStringLiteral extends AbstractException
{
public static function new(int $offset): self
{
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class SequenceDoesNotExist extends AbstractException
{
public static function new(): self
{
return new self('lastInsertId failed: Query was executed but no result was returned.');
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class UnknownParameterIndex extends AbstractException
{
public static function new(int $index): self
{
return new self(
sprintf('Could not find variable mapping with index %d, in the SQL statement', $index)
);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* @psalm-immutable
*/
class OCI8Exception extends AbstractDriverException
{
/**
* @param mixed[]|false $error
*/
public static function fromErrorInfo($error): self
{
if ($error === false) {
return new self('Database error occurred but no error information was retrieved from the driver.');
}
return new self($error['message'], null, $error['code']);
}
}
......@@ -26,6 +26,8 @@ final class Result implements ResultInterface
private $statement;
/**
* @internal The result can be only instantiated by its driver connection or statement.
*
* @param resource $statement
*/
public function __construct($statement)
......
......@@ -4,19 +4,20 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\OCI8\Exception\Error;
use Doctrine\DBAL\Driver\OCI8\Exception\UnknownParameterIndex;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
use function is_int;
use function is_resource;
use function oci_bind_by_name;
use function oci_error;
use function oci_execute;
use function oci_new_descriptor;
use function oci_parse;
use function sprintf;
use const OCI_B_BIN;
use const OCI_B_BLOB;
......@@ -26,10 +27,7 @@ use const OCI_NO_AUTO_COMMIT;
use const OCI_TEMP_BLOB;
use const SQLT_CHR;
/**
* The OCI8 implementation of the Statement interface.
*/
final class OCI8Statement implements Statement
final class Statement implements StatementInterface
{
/** @var resource */
private $connection;
......@@ -55,10 +53,12 @@ final class OCI8Statement implements Statement
/**
* Creates a new OCI8Statement that uses the given connection handle and SQL statement.
*
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $dbh The connection handle.
* @param string $query The SQL query.
*
* @throws OCI8Exception
* @throws Exception
*/
public function __construct($dbh, string $query, ExecutionMode $executionMode)
{
......@@ -88,7 +88,7 @@ final class OCI8Statement implements Statement
{
if (is_int($param)) {
if (! isset($this->parameterMap[$param])) {
throw new OCI8Exception(sprintf('Could not find variable mapping with index %d, in the SQL statement', $param));
throw UnknownParameterIndex::new($param);
}
$param = $this->parameterMap[$param];
......@@ -116,7 +116,7 @@ final class OCI8Statement implements Statement
$this->convertParameterType($type)
)
) {
throw OCI8Exception::fromErrorInfo(oci_error($this->statement));
throw Error::new($this->statement);
}
}
......@@ -162,7 +162,7 @@ final class OCI8Statement implements Statement
$ret = @oci_execute($this->statement, $mode);
if (! $ret) {
throw OCI8Exception::fromErrorInfo(oci_error($this->statement));
throw Error::new($this->statement);
}
return new Result($this->statement);
......
......@@ -2,49 +2,50 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\PDO\Result;
use Doctrine\DBAL\Driver\Exception as ExceptionInterface;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use PDO;
use PDOException;
use PDOStatement;
use function assert;
/**
* PDO implementation of the Connection interface.
*
* Used by all PDO-based drivers.
*/
class PDOConnection implements ServerInfoAwareConnection
final class Connection implements ServerInfoAwareConnection
{
/** @var PDO */
private $connection;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param array<int, mixed> $options
*
* @throws PDOException In case of an error.
* @throws ExceptionInterface
*/
public function __construct(string $dsn, string $username = '', string $password = '', array $options = [])
{
try {
$this->connection = new PDO($dsn, $username, $password, $options);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
public function exec(string $statement): int
public function exec(string $sql): int
{
try {
$result = $this->connection->exec($statement);
$result = $this->connection->exec($sql);
assert($result !== false);
return $result;
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
......@@ -53,14 +54,19 @@ class PDOConnection implements ServerInfoAwareConnection
return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION);
}
public function prepare(string $sql): Statement
/**
* {@inheritDoc}
*
* @return Statement
*/
public function prepare(string $sql): StatementInterface
{
try {
return $this->createStatement(
$this->connection->prepare($sql)
);
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
......@@ -68,11 +74,11 @@ class PDOConnection implements ServerInfoAwareConnection
{
try {
$stmt = $this->connection->query($sql);
assert($stmt instanceof \PDOStatement);
assert($stmt instanceof PDOStatement);
return new Result($stmt);
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
......@@ -89,17 +95,17 @@ class PDOConnection implements ServerInfoAwareConnection
}
return $this->connection->lastInsertId($name);
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* Creates a wrapped statement
*/
protected function createStatement(\PDOStatement $stmt): PDOStatement
protected function createStatement(PDOStatement $stmt): Statement
{
return new PDOStatement($stmt);
return new Statement($stmt);
}
public function beginTransaction(): void
......
......@@ -2,19 +2,19 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\AbstractException;
use PDOException;
/**
* Tiny wrapper for PDOException instances to implement the {@link DriverException} interface.
* @internal
*
* @psalm-immutable
*/
class PDOException extends AbstractDriverException
final class Exception extends AbstractException
{
/**
* @param \PDOException $exception The PDO exception to wrap.
*/
public function __construct(\PDOException $exception)
public static function new(PDOException $exception): self
{
if ($exception->errorInfo !== null) {
[$sqlState, $code] = $exception->errorInfo;
......@@ -23,6 +23,6 @@ class PDOException extends AbstractDriverException
$sqlState = null;
}
parent::__construct($exception->getMessage(), $sqlState, $code, $exception);
return new self($exception->getMessage(), $sqlState, $code, $exception);
}
}
......@@ -2,22 +2,19 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOMySql;
namespace Doctrine\DBAL\Driver\PDO\MySQL;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDO\Connection;
use PDO;
/**
* PDO MySql driver.
*/
final class Driver extends AbstractMySQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params): Connection
public function connect(array $params): ConnectionInterface
{
$driverOptions = $params['driver_options'] ?? [];
......@@ -25,7 +22,7 @@ final class Driver extends AbstractMySQLDriver
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
return new PDOConnection(
return new Connection(
$this->constructPdoDsn($params),
$params['user'] ?? '',
$params['password'] ?? '',
......
......@@ -2,27 +2,19 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOOracle;
namespace Doctrine\DBAL\Driver\PDO\OCI;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDO\Connection;
use PDO;
/**
* PDO Oracle driver.
*
* WARNING: This driver gives us segfaults in our testsuites on CLOB and other
* stuff. PDO Oracle is not maintained by Oracle or anyone in the PHP community,
* which leads us to the recommendation to use the "oci8" driver to connect
* to Oracle instead.
*/
final class Driver extends AbstractOracleDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params): Connection
public function connect(array $params): ConnectionInterface
{
$driverOptions = $params['driver_options'] ?? [];
......@@ -30,7 +22,7 @@ final class Driver extends AbstractOracleDriver
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
return new PDOConnection(
return new Connection(
$this->constructPdoDsn($params),
$params['user'] ?? '',
$params['password'] ?? '',
......
......@@ -2,24 +2,21 @@
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOPgSql;
namespace Doctrine\DBAL\Driver\PDO\PgSQL;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDO\Connection;
use PDO;
use function defined;
/**
* Driver that connects through pdo_pgsql.
*/
final class Driver extends AbstractPostgreSQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params): Connection
public function connect(array $params): ConnectionInterface
{
$driverOptions = $params['driver_options'] ?? [];
......@@ -27,7 +24,7 @@ final class Driver extends AbstractPostgreSQLDriver
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
$connection = new PDOConnection(
$connection = new Connection(
$this->constructPdoDsn($params),
$params['user'] ?? '',
$params['password'] ?? '',
......
......@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\PDOException;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use PDO;
use PDOException;
use PDOStatement;
use function assert;
......@@ -17,6 +17,9 @@ final class Result implements ResultInterface
/** @var PDOStatement */
private $statement;
/**
* @internal The result can be only instantiated by its driver connection or statement.
*/
public function __construct(PDOStatement $statement)
{
$this->statement = $statement;
......@@ -74,8 +77,8 @@ final class Result implements ResultInterface
{
try {
return $this->statement->rowCount();
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
......@@ -83,45 +86,41 @@ final class Result implements ResultInterface
{
try {
return $this->statement->columnCount();
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
public function free(): void
{
try {
$this->statement->closeCursor();
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
$this->statement->closeCursor();
}
/**
* @return mixed|false
*
* @throws PDOException
* @throws Exception
*/
private function fetch(int $mode)
{
try {
return $this->statement->fetch($mode);
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* @return array<int,mixed>
*
* @throws PDOException
* @throws Exception
*/
private function fetchAll(int $mode): array
{
try {
$data = $this->statement->fetchAll($mode);
} catch (\PDOException $exception) {
throw new PDOException($exception);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
assert(is_array($data));
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO\SQLSrv;
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use PDO;
use function strpos;
use function substr;
final class Connection implements ServerInfoAwareConnection
{
/** @var PDOConnection */
private $connection;
public function __construct(PDOConnection $connection)
{
$this->connection = $connection;
}
public function prepare(string $sql): StatementInterface
{
return new Statement(
$this->connection->prepare($sql)
);
}
public function query(string $sql): Result
{
return $this->connection->query($sql);
}
public function quote(string $value): string
{
$val = $this->connection->quote($value);
// Fix for a driver version terminating all values with null byte
if (strpos($val, "\0") !== false) {
$val = substr($val, 0, -1);
}
return $val;
}
public function exec(string $sql): int
{
return $this->connection->exec($sql);
}
public function lastInsertId(?string $name = null): string
{
if ($name === null) {
return $this->connection->lastInsertId($name);
}
return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?')
->execute([$name])
->fetchOne();
}
public function beginTransaction(): void
{
$this->connection->beginTransaction();
}
public function commit(): void
{
$this->connection->commit();
}
public function rollBack(): void
{
$this->connection->rollBack();
}
public function getServerVersion(): string
{
return $this->connection->getServerVersion();
}
public function getWrappedConnection(): PDO
{
return $this->connection->getWrappedConnection();
}
}
This diff is collapsed.
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
/**
* An interface for connections which support a "native" ping method.
*/
interface PingableConnection extends Connection
{
/**
* Pings the database server to determine if the connection is still
* available.
*
* @throws DriverException
*/
public function ping(): void;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment