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

Merge branch '3.0.x'

parents 190a7afb 4cc12da9
...@@ -102,3 +102,87 @@ jobs: ...@@ -102,3 +102,87 @@ jobs:
- name: "Run squizlabs/php_codesniffer" - name: "Run squizlabs/php_codesniffer"
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" 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 ...@@ -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 [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 [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 [Master]: https://travis-ci.org/doctrine/dbal
[CodeCov Master]: https://codecov.io/gh/doctrine/dbal/branch/master [CodeCov Master]: https://codecov.io/gh/doctrine/dbal/branch/master
[AppVeyor master]: https://ci.appveyor.com/project/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 [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 [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 [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 [2.10]: https://github.com/doctrine/dbal/tree/2.10.x
[CodeCov 2.10]: https://codecov.io/gh/doctrine/dbal/branch/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 [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 @@ ...@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "f87aeb14c3190629c5d6b76523ea7bf1", "content-hash": "acad7f4e4727e01178c00f5809ed760b",
"packages": [ "packages": [
{ {
"name": "composer/package-versions-deprecated", "name": "composer/package-versions-deprecated",
...@@ -481,22 +481,22 @@ ...@@ -481,22 +481,22 @@
}, },
{ {
"name": "dealerdirect/phpcodesniffer-composer-installer", "name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.6.2", "version": "v0.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a" "reference": "e8d808670b8f882188368faaf1144448c169c0b7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/8001af8eb107fbfcedc31a8b51e20b07d85b457a", "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7",
"reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a", "reference": "e8d808670b8f882188368faaf1144448c169c0b7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"composer-plugin-api": "^1.0", "composer-plugin-api": "^1.0 || ^2.0",
"php": "^5.3|^7", "php": ">=5.3",
"squizlabs/php_codesniffer": "^2|^3" "squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev"
}, },
"require-dev": { "require-dev": {
"composer/composer": "*", "composer/composer": "*",
...@@ -543,26 +543,26 @@ ...@@ -543,26 +543,26 @@
"stylecheck", "stylecheck",
"tests" "tests"
], ],
"time": "2020-01-29T20:22:20+00:00" "time": "2020-06-25T14:57:39+00:00"
}, },
{ {
"name": "doctrine/coding-standard", "name": "doctrine/coding-standard",
"version": "8.0.0", "version": "8.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/coding-standard.git", "url": "https://github.com/doctrine/coding-standard.git",
"reference": "742200e29fb8ffd58ba9abec8a9ec33db0884677" "reference": "637003febec655f1b27f4301b44bf2264be57434"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/coding-standard/zipball/742200e29fb8ffd58ba9abec8a9ec33db0884677", "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/637003febec655f1b27f4301b44bf2264be57434",
"reference": "742200e29fb8ffd58ba9abec8a9ec33db0884677", "reference": "637003febec655f1b27f4301b44bf2264be57434",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2", "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
"php": "^7.2 || ^8.0", "php": "^7.2 || ^8.0",
"slevomat/coding-standard": "^6.3.8", "slevomat/coding-standard": "^6.3.9",
"squizlabs/php_codesniffer": "^3.5.5" "squizlabs/php_codesniffer": "^3.5.5"
}, },
"type": "phpcodesniffer-standard", "type": "phpcodesniffer-standard",
...@@ -599,7 +599,7 @@ ...@@ -599,7 +599,7 @@
"standard", "standard",
"style" "style"
], ],
"time": "2020-06-02T17:58:43+00:00" "time": "2020-07-05T20:35:22+00:00"
}, },
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",
...@@ -1362,20 +1362,23 @@ ...@@ -1362,20 +1362,23 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "0.12.18", "version": "0.12.33",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "1ce27fe29c8660a27926127d350d53d80c4d4286" "reference": "46e698a0452526a05c4a351d7c47c4b8c37a548d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1ce27fe29c8660a27926127d350d53d80c4d4286", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e698a0452526a05c4a351d7c47c4b8c37a548d",
"reference": "1ce27fe29c8660a27926127d350d53d80c4d4286", "reference": "46e698a0452526a05c4a351d7c47c4b8c37a548d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1" "php": "^7.1|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
}, },
"bin": [ "bin": [
"phpstan", "phpstan",
...@@ -1397,7 +1400,21 @@ ...@@ -1397,7 +1400,21 @@
"MIT" "MIT"
], ],
"description": "PHPStan - PHP Static Analysis Tool", "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", "name": "phpstan/phpstan-phpunit",
...@@ -2784,16 +2801,16 @@ ...@@ -2784,16 +2801,16 @@
}, },
{ {
"name": "slevomat/coding-standard", "name": "slevomat/coding-standard",
"version": "6.3.8", "version": "6.3.10",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/slevomat/coding-standard.git", "url": "https://github.com/slevomat/coding-standard.git",
"reference": "500f55b5e2dee1dcef8e88f2038990acdba29f1c" "reference": "58fa5ea2c048357ae55185eb5e93ca2826fffde0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/500f55b5e2dee1dcef8e88f2038990acdba29f1c", "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/58fa5ea2c048357ae55185eb5e93ca2826fffde0",
"reference": "500f55b5e2dee1dcef8e88f2038990acdba29f1c", "reference": "58fa5ea2c048357ae55185eb5e93ca2826fffde0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -2837,7 +2854,7 @@ ...@@ -2837,7 +2854,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2020-05-27T06:28:47+00:00" "time": "2020-06-22T11:33:09+00:00"
}, },
{ {
"name": "squizlabs/php_codesniffer", "name": "squizlabs/php_codesniffer",
......
Architecture Architecture
============ ============
As already said, the DBAL is a thin layer on top of PDO. PDO itself The DBAL consists of two layers: drivers and a wrapper. Each layer
is mainly defined in terms of 2 classes: ``PDO`` and is mainly defined in terms of 3 components: ``Connection``,
``PDOStatement``. The equivalent classes in the DBAL are ``Statement`` and ``Result``.
``Doctrine\DBAL\Connection`` and ``Doctrine\DBAL\Statement``. A A ``Doctrine\DBAL\Connection`` wraps a ``Doctrine\DBAL\Driver\Connection``,
``Doctrine\DBAL\Connection`` wraps a a ``Doctrine\DBAL\Statement`` wraps a ``Doctrine\DBAL\Driver\Statement``
``Doctrine\DBAL\Driver\Connection`` and a and a ``Doctrine\DBAL\Result`` wraps a ``Doctrine\DBAL\Driver\Result``.
``Doctrine\DBAL\Statement`` wraps a
``Doctrine\DBAL\Driver\Statement``. ``Doctrine\DBAL\Driver\Connection``, ``Doctrine\DBAL\Driver\Statement``
and ``Doctrine\DBAL\Driver\Result`` are just interfaces.
``Doctrine\DBAL\Driver\Connection`` and These interfaces are implemented by concrete drivers.
``Doctrine\DBAL\Driver\Statement`` are just interfaces. These
interfaces are implemented by concrete drivers. For all PDO based What do the wrapper components add to the underlying driver
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
implementations? The enhancements include SQL logging, events and implementations? The enhancements include SQL logging, events and
control over the transaction isolation level in a portable manner, control over the transaction isolation level in a portable manner,
among others. among others.
A DBAL driver is defined to the outside in terms of 3 interfaces: Apart from the three main components, a DBAL driver should also provide
``Doctrine\DBAL\Driver``, ``Doctrine\DBAL\Driver\Connection`` and an implementation of the ``Doctrine\DBAL\Driver`` interface that
``Doctrine\DBAL\Driver\Statement``. The latter two resemble (a has two primary purposes:
subset of) the corresponding PDO API.
A concrete driver implementation must provide implementation 1. Translate the DBAL connection parameters to the ones specific
classes for these 3 interfaces. 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 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 Drivers
------- -------
The drivers abstract a PHP specific database API by enforcing two The drivers abstract a PHP specific database API by enforcing three
interfaces: interfaces:
- ``\Doctrine\DBAL\Driver\Connection`` - ``\Doctrine\DBAL\Driver\Connection``
- ``\Doctrine\DBAL\Driver\Statement`` - ``\Doctrine\DBAL\Driver\Statement``
- ``\Doctrine\DBAL\Driver\Result``
The above two interfaces require exactly the same methods as PDO.
Platforms Platforms
--------- ---------
...@@ -59,8 +48,8 @@ features a platform supports. The ...@@ -59,8 +48,8 @@ features a platform supports. The
denominator of what a database platform has to publish to the denominator of what a database platform has to publish to the
userland, to be fully supportable by Doctrine. This includes the userland, to be fully supportable by Doctrine. This includes the
SchemaTool, Transaction Isolation and many other features. The SchemaTool, Transaction Isolation and many other features. The
Database platform for MySQL for example can be used by all 3 MySQL Database platform for MySQL for example can be used by multiple
extensions, PDO, Mysqli and ext/mysql. MySQL extensions: pdo_mysql and mysqli.
Logging Logging
------- -------
......
...@@ -142,7 +142,7 @@ use prepared statements: ...@@ -142,7 +142,7 @@ use prepared statements:
SQL query, bind the given params with their binding types and execute the query. 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 This method returns the executed prepared statement for iteration and is useful
for SELECT statements. 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. 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 This method returns the number of affected rows by the executed query and is useful
for UPDATE, DELETE and INSERT statements. for UPDATE, DELETE and INSERT statements.
...@@ -170,7 +170,7 @@ of this query using the fetch API of a statement: ...@@ -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. 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 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. methods. See the API section below on details how to use them.
Additionally there are lots of convenience methods for data-retrieval and manipulation 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. ...@@ -208,7 +208,7 @@ which means this code works independent of the database you are using.
.. note:: .. note::
Be aware this type conversion only works with ``Statement#bindValue()``, 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()``, is not supported to pass a doctrine type name to ``Statement#bindParam()``,
because this would not work with binding by reference. because this would not work with binding by reference.
...@@ -286,7 +286,7 @@ This is much more complicated and is ugly to write generically. ...@@ -286,7 +286,7 @@ This is much more complicated and is ugly to write generically.
.. note:: .. note::
The parameter list support only works with ``Doctrine\DBAL\Connection::executeQuery()`` 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. a prepared statement.
API API
...@@ -319,7 +319,7 @@ Prepare a given SQL statement and return the ...@@ -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 Executes a prepared statement with the given SQL and parameters and
...@@ -328,7 +328,7 @@ returns the affected rows count: ...@@ -328,7 +328,7 @@ returns the affected rows count:
.. code-block:: php .. code-block:: php
<?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 echo $count; // 1
The ``$types`` variable contains the PDO or Doctrine Type constants 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 ...@@ -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 = ?"; $sql = "SELECT * FROM users WHERE username = ?";
$stmt = $connection->executeQuery($sql, array($_GET['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 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 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 @@ ...@@ -89,8 +89,8 @@
phpcs wrongly complains about them, and that has been reported here: phpcs wrongly complains about them, and that has been reported here:
https://github.com/squizlabs/PHP_CodeSniffer/issues/2950 https://github.com/squizlabs/PHP_CodeSniffer/issues/2950
--> -->
<exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern> <exclude-pattern>src/Driver/IBMDB2/Connection.php</exclude-pattern>
<exclude-pattern>src/Driver/Mysqli/MysqliConnection.php</exclude-pattern> <exclude-pattern>src/Driver/Mysqli/Exception/ConnectionFailed.php</exclude-pattern>
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 --> <!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<exclude-pattern>src/SQLParserUtils.php</exclude-pattern> <exclude-pattern>src/SQLParserUtils.php</exclude-pattern>
<exclude-pattern>src/Tools/Dumper.php</exclude-pattern> <exclude-pattern>src/Tools/Dumper.php</exclude-pattern>
...@@ -104,9 +104,10 @@ ...@@ -104,9 +104,10 @@
<exclude-pattern>tests/Functional/ResultCacheTest.php</exclude-pattern> <exclude-pattern>tests/Functional/ResultCacheTest.php</exclude-pattern>
</rule> </rule>
<!-- See https://github.com/slevomat/coding-standard/issues/1038 --> <!-- See https://github.com/slevomat/coding-standard/issues/770 -->
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses"> <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> </rule>
<!-- see https://github.com/doctrine/dbal/issues/3377 --> <!-- see https://github.com/doctrine/dbal/issues/3377 -->
...@@ -114,15 +115,9 @@ ...@@ -114,15 +115,9 @@
<exclude-pattern>src/Schema/Comparator.php</exclude-pattern> <exclude-pattern>src/Schema/Comparator.php</exclude-pattern>
</rule> </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 <!-- 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 --> see https://docs.microsoft.com/en-us/sql/connect/php/constants-microsoft-drivers-for-php-for-sql-server -->
<rule ref="Squiz.PHP.LowercasePHPFunctions"> <rule ref="Squiz.PHP.LowercasePHPFunctions">
<exclude-pattern>src/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern> <exclude-pattern>src/Driver/SQLSrv/Statement.php</exclude-pattern>
</rule> </rule>
</ruleset> </ruleset>
...@@ -3,19 +3,13 @@ parameters: ...@@ -3,19 +3,13 @@ parameters:
paths: paths:
- %currentWorkingDirectory%/src - %currentWorkingDirectory%/src
- %currentWorkingDirectory%/tests - %currentWorkingDirectory%/tests
autoload_files: scanFiles:
- %currentWorkingDirectory%/tests/phpstan-polyfill.php - %currentWorkingDirectory%/tests/phpstan-polyfill.php
treatPhpDocTypesAsCertain: false treatPhpDocTypesAsCertain: false
reportUnmatchedIgnoredErrors: false reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false checkGenericClassInNonGenericObjectType: false
ignoreErrors: 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 # legacy remnants from doctrine/common
- '~^Class Doctrine\\Common\\(Collections\\Collection|Persistence\\Proxy) not found\.\z~' - '~^Class Doctrine\\Common\\(Collections\\Collection|Persistence\\Proxy) not found\.\z~'
- '~^.+ on an unknown class Doctrine\\Common\\(Collections\\Collection|Persistence\\Proxy)\.\z~' - '~^.+ on an unknown class Doctrine\\Common\\(Collections\\Collection|Persistence\\Proxy)\.\z~'
...@@ -26,24 +20,6 @@ parameters: ...@@ -26,24 +20,6 @@ parameters:
# weird class name, represented in stubs as OCI_(Lob|Collection) # weird class name, represented in stubs as OCI_(Lob|Collection)
- '~unknown class 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 # https://github.com/phpstan/phpstan/issues/3133
- -
message: '~^Cannot cast array<string>\|bool\|string\|null to int\.$~' message: '~^Cannot cast array<string>\|bool\|string\|null to int\.$~'
...@@ -52,19 +28,12 @@ parameters: ...@@ -52,19 +28,12 @@ parameters:
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/553 # Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/553
- -
message: '~^Call to function assert\(\) with true will always evaluate to true\.$~' 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 # 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\.$~' message: '~^Strict comparison using !== between int and false 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/732
-
message: '~^Access to undefined constant PDO::PGSQL_ATTR_DISABLE_PREPARES\.$~'
paths:
- %currentWorkingDirectory%/src/Driver/PDOPgSql/Driver.php
- %currentWorkingDirectory%/tests/Driver/PDOPgSql/DriverTest.php
# False Positive # False Positive
- '~Strict comparison using === between 1 and 2 will always evaluate to false~' - '~Strict comparison using === between 1 and 2 will always evaluate to false~'
...@@ -81,7 +50,6 @@ parameters: ...@@ -81,7 +50,6 @@ parameters:
- -
message: '~^Construct empty\(\) is not allowed. Use more strict comparison\.~' message: '~^Construct empty\(\) is not allowed. Use more strict comparison\.~'
paths: paths:
- %currentWorkingDirectory%/src/Driver/*/*Connection.php
- %currentWorkingDirectory%/src/Driver/*/Driver.php - %currentWorkingDirectory%/src/Driver/*/Driver.php
- %currentWorkingDirectory%/src/Driver/AbstractOracleDriver/EasyConnectString.php - %currentWorkingDirectory%/src/Driver/AbstractOracleDriver/EasyConnectString.php
- %currentWorkingDirectory%/src/Platforms/*Platform.php - %currentWorkingDirectory%/src/Platforms/*Platform.php
...@@ -110,27 +78,16 @@ parameters: ...@@ -110,27 +78,16 @@ parameters:
# Temporaily suppressed during up-merging an upgrade PHPStan 0.12 # 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 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 # Temporaily suppressed during up-merging an upgrade PHPStan 0.12
- -
message: '~^Call to an undefined method Doctrine\\DBAL\\Driver::createDatabasePlatformForVersion\(\)\.$~' message: '~^Call to an undefined method Doctrine\\DBAL\\Driver::createDatabasePlatformForVersion\(\)\.$~'
path: %currentWorkingDirectory%/tests/Driver/AbstractDriverTest.php path: %currentWorkingDirectory%/tests/Driver/AbstractDriverTest.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\\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
- -
message: '~^Method Doctrine\\DBAL\\Tests\\Functional\\Driver\\PDOSqlsrv\\DriverTest::getConnection\(\) should return Doctrine\\DBAL\\Driver\\PDOConnection but returns Doctrine\\DBAL\\Driver\\Connection\.$~' 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/Functional/Driver/PDOSqlsrv/DriverTest.php path: %currentWorkingDirectory%/tests/DriverManagerTest.php
# Caused by phpdoc annotations intended for Psalm # Caused by phpdoc annotations intended for Psalm
- -
...@@ -141,9 +98,7 @@ parameters: ...@@ -141,9 +98,7 @@ parameters:
- %currentWorkingDirectory%/tests/ConnectionTest.php - %currentWorkingDirectory%/tests/ConnectionTest.php
- %currentWorkingDirectory%/tests/DriverManagerTest.php - %currentWorkingDirectory%/tests/DriverManagerTest.php
- %currentWorkingDirectory%/tests/Functional/ConnectionTest.php - %currentWorkingDirectory%/tests/Functional/ConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/PDOPgsqlConnectionTest.php - %currentWorkingDirectory%/tests/Functional/Driver/PDO/PgSQL/ConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/SQLAnywhere/ConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/SQLAnywhere/StatementTest.php
- %currentWorkingDirectory%/tests/Functional/ExceptionTest.php - %currentWorkingDirectory%/tests/Functional/ExceptionTest.php
- %currentWorkingDirectory%/tests/Functional/PortabilityTest.php - %currentWorkingDirectory%/tests/Functional/PortabilityTest.php
- %currentWorkingDirectory%/tests/Functional/PrimaryReadReplicaConnectionTest.php - %currentWorkingDirectory%/tests/Functional/PrimaryReadReplicaConnectionTest.php
...@@ -151,15 +106,6 @@ parameters: ...@@ -151,15 +106,6 @@ parameters:
- %currentWorkingDirectory%/tests/Schema/Synchronizer/SingleDatabaseSynchronizerTest.php - %currentWorkingDirectory%/tests/Schema/Synchronizer/SingleDatabaseSynchronizerTest.php
- %currentWorkingDirectory%/tests/TestUtil.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: includes:
- vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon - vendor/phpstan/phpstan-phpunit/rules.neon
......
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
<!-- <!--
Fixing these issues requires an API change Fixing these issues requires an API change
--> -->
<file name="src/Driver/PDOSqlsrv/Connection.php"/> <file name="src/Driver/PDO/SQLSrv/Connection.php"/>
<file name="src/Driver/SQLSrv/SQLSrvConnection.php"/> <file name="src/Driver/SQLSrv/Connection.php"/>
</errorLevel> </errorLevel>
</FalsableReturnStatement> </FalsableReturnStatement>
<ImpureMethodCall> <ImpureMethodCall>
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
Requires a release of Requires a release of
https://github.com/JetBrains/phpstorm-stubs/pull/727 https://github.com/JetBrains/phpstorm-stubs/pull/727
--> -->
<file name="src/Driver/SQLSrv/SQLSrvStatement.php"/> <file name="src/Driver/SQLSrv/Statement.php"/>
</errorLevel> </errorLevel>
</TooFewArguments> </TooFewArguments>
<UndefinedConstant> <UndefinedConstant>
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
Requires a release of Requires a release of
https://github.com/JetBrains/phpstorm-stubs/pull/732 https://github.com/JetBrains/phpstorm-stubs/pull/732
--> -->
<file name="tests/Driver/PDOPgSql/DriverTest.php" /> <file name="tests/Driver/PDO/PgSQL/DriverTest.php" />
</errorLevel> </errorLevel>
</UndefinedConstant> </UndefinedConstant>
<UndefinedClass> <UndefinedClass>
......
...@@ -5,7 +5,7 @@ declare(strict_types=1); ...@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Cache; namespace Doctrine\DBAL\Cache;
use Doctrine\Common\Cache\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\FetchUtils;
use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\Result;
...@@ -130,7 +130,7 @@ final class CachingResult implements Result ...@@ -130,7 +130,7 @@ final class CachingResult implements Result
/** /**
* @return array<string,mixed>|false * @return array<string,mixed>|false
* *
* @throws DriverException * @throws Exception
*/ */
private function fetch() private function fetch()
{ {
......
...@@ -65,9 +65,9 @@ class QueryCacheProfile ...@@ -65,9 +65,9 @@ class QueryCacheProfile
* *
* @return string[] * @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) . '&params=' . serialize($params) .
'&types=' . serialize($types) . '&types=' . serialize($types) .
'&connectionParams=' . hash('sha256', serialize($connectionParams)); '&connectionParams=' . hash('sha256', serialize($connectionParams));
......
...@@ -11,15 +11,11 @@ final class ColumnCase ...@@ -11,15 +11,11 @@ final class ColumnCase
{ {
/** /**
* Convert column names to upper case. * Convert column names to upper case.
*
* @see \PDO::CASE_UPPER
*/ */
public const UPPER = 1; public const UPPER = 1;
/** /**
* Convert column names to lower case. * Convert column names to lower case.
*
* @see \PDO::CASE_LOWER
*/ */
public const LOWER = 2; public const LOWER = 2;
......
...@@ -5,17 +5,20 @@ declare(strict_types=1); ...@@ -5,17 +5,20 @@ declare(strict_types=1);
namespace Doctrine\DBAL; namespace Doctrine\DBAL;
use Doctrine\Common\Cache\Cache; use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Logging\NullLogger; use Doctrine\DBAL\Logging\NullLogger;
use Doctrine\DBAL\Logging\SQLLogger; use Doctrine\DBAL\Logging\SQLLogger;
/** /**
* Configuration container for the Doctrine DBAL. * Configuration container for the Doctrine DBAL.
* *
* @internal When adding a new configuration option just write a getter/setter * @internal
* pair and add the option to the _attributes array with a proper default value.
*/ */
class Configuration class Configuration
{ {
/** @var Middleware[] */
private $middlewares = [];
/** /**
* The attributes that are contained in the configuration. * The attributes that are contained in the configuration.
* Values are default values. * Values are default values.
...@@ -101,4 +104,24 @@ class Configuration ...@@ -101,4 +104,24 @@ class Configuration
{ {
return $this->_attributes['autoCommit'] ?? true; 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; ...@@ -10,17 +10,14 @@ use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\DriverException; use Doctrine\DBAL\Driver\Exception as DriverException;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events; use Doctrine\DBAL\Events;
use Doctrine\DBAL\Statement;
use InvalidArgumentException; use InvalidArgumentException;
use function array_rand; use function array_rand;
use function assert;
use function count; use function count;
use function func_get_args;
/** /**
* Primary-Replica Connection * Primary-Replica Connection
...@@ -32,9 +29,8 @@ use function func_get_args; ...@@ -32,9 +29,8 @@ use function func_get_args;
* *
* 1. Replica if primary was never picked before and ONLY if 'getWrappedConnection' * 1. Replica if primary was never picked before and ONLY if 'getWrappedConnection'
* or 'executeQuery' is used. * or 'executeQuery' is used.
* 2. Primary picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint', * 2. Primary picked when 'executeStatement', 'insert', 'delete', 'update', 'createSavepoint',
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit' or 'prepare' is called.
* 'prepare' is called.
* 3. If Primary was picked once during the lifetime of the connection it will always get picked afterwards. * 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. * 4. One replica connection is randomly picked ONCE during a request.
* *
...@@ -47,7 +43,7 @@ use function func_get_args; ...@@ -47,7 +43,7 @@ use function func_get_args;
* Be aware that Connection#executeQuery is a method specifically for READ * Be aware that Connection#executeQuery is a method specifically for READ
* operations only. * 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). * state in the database (UPDATE, INSERT, DELETE or DDL statements).
* *
* This connection is limited to replica operations using the * This connection is limited to replica operations using the
...@@ -97,8 +93,11 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -97,8 +93,11 @@ class PrimaryReadReplicaConnection extends Connection
/** /**
* Creates Primary Replica Connection. * Creates Primary Replica Connection.
* *
* @internal The connection can be only instantiated by the driver manager.
*
* @param array<string, mixed> $params * @param array<string, mixed> $params
* *
* @throws DBALException
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function __construct( public function __construct(
...@@ -218,6 +217,8 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -218,6 +217,8 @@ class PrimaryReadReplicaConnection extends Connection
/** /**
* Connects to a specific connection. * Connects to a specific connection.
*
* @throws DBALException
*/ */
protected function connectTo(string $connectionName): DriverConnection protected function connectTo(string $connectionName): DriverConnection
{ {
...@@ -228,7 +229,7 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -228,7 +229,7 @@ class PrimaryReadReplicaConnection extends Connection
try { try {
return $this->_driver->connect($connectionParams); return $this->_driver->connect($connectionParams);
} catch (DriverException $e) { } catch (DriverException $e) {
throw DBALException::driverException($this->_driver, $e); throw $this->convertException($e);
} }
} }
...@@ -255,11 +256,11 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -255,11 +256,11 @@ class PrimaryReadReplicaConnection extends Connection
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function executeUpdate(string $query, array $params = [], array $types = []): int public function executeStatement($sql, array $params = [], array $types = []): int
{ {
$this->ensureConnectedToPrimary(); $this->ensureConnectedToPrimary();
return parent::executeUpdate($query, $params, $types); return parent::executeStatement($sql, $params, $types);
} }
public function beginTransaction(): void public function beginTransaction(): void
...@@ -283,16 +284,6 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -283,16 +284,6 @@ class PrimaryReadReplicaConnection extends Connection
parent::rollBack(); 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 public function close(): void
{ {
unset($this->connections['primary'], $this->connections['replica']); unset($this->connections['primary'], $this->connections['replica']);
...@@ -303,33 +294,6 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -303,33 +294,6 @@ class PrimaryReadReplicaConnection extends Connection
$this->connections = ['primary' => null, 'replica' => null]; $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 public function createSavepoint(string $savepoint): void
{ {
$this->ensureConnectedToPrimary(); $this->ensureConnectedToPrimary();
...@@ -351,23 +315,6 @@ class PrimaryReadReplicaConnection extends Connection ...@@ -351,23 +315,6 @@ class PrimaryReadReplicaConnection extends Connection
parent::rollbackSavepoint($savepoint); 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 public function prepare(string $sql): Statement
{ {
$this->ensureConnectedToPrimary(); $this->ensureConnectedToPrimary();
......
...@@ -4,86 +4,11 @@ declare(strict_types=1); ...@@ -4,86 +4,11 @@ declare(strict_types=1);
namespace Doctrine\DBAL; namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
use Doctrine\DBAL\Exception\DriverException;
use Exception; 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 * @psalm-immutable
*/ */
class DBALException extends Exception 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); ...@@ -4,8 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL; namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Driver\Connection as DriverConnection; 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\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\AbstractSchemaManager;
...@@ -22,7 +23,7 @@ interface Driver ...@@ -22,7 +23,7 @@ interface Driver
* *
* @return DriverConnection The database connection. * @return DriverConnection The database connection.
* *
* @throws DriverException * @throws Exception
*/ */
public function connect(array $params): DriverConnection; public function connect(array $params): DriverConnection;
...@@ -38,5 +39,10 @@ interface Driver ...@@ -38,5 +39,10 @@ interface Driver
* Gets the SchemaManager that can be used to inspect and change the underlying * Gets the SchemaManager that can be used to inspect and change the underlying
* database schema of the platform this driver connects to. * 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; ...@@ -6,13 +6,15 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; 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\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\DB2SchemaManager; 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 abstract class AbstractDB2Driver implements Driver
{ {
...@@ -21,8 +23,13 @@ abstract class AbstractDB2Driver implements Driver ...@@ -21,8 +23,13 @@ abstract class AbstractDB2Driver implements Driver
return new DB2Platform(); 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); ...@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver; namespace Doctrine\DBAL\Driver;
use Exception; use Exception as BaseException;
use Throwable; use Throwable;
/** /**
...@@ -12,7 +12,7 @@ use Throwable; ...@@ -12,7 +12,7 @@ use Throwable;
* *
* @psalm-immutable * @psalm-immutable
*/ */
abstract class AbstractDriverException extends Exception implements DriverException abstract class AbstractException extends BaseException implements Exception
{ {
/** /**
* The SQLSTATE of the driver. * The SQLSTATE of the driver.
......
...@@ -6,9 +6,8 @@ namespace Doctrine\DBAL\Driver; ...@@ -6,9 +6,8 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception; use Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion; use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion;
use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MariaDb1027Platform;
...@@ -24,157 +23,10 @@ use function stripos; ...@@ -24,157 +23,10 @@ use function stripos;
use function version_compare; 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} * {@inheritdoc}
* *
...@@ -276,8 +128,13 @@ abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionA ...@@ -276,8 +128,13 @@ abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionA
* *
* @return MySqlSchemaManager * @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; ...@@ -7,67 +7,31 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString; use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception; use Doctrine\DBAL\Driver\API\OCI;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\OracleSchemaManager; 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()) { return new OraclePlatform();
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);
} }
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); ...@@ -5,9 +5,8 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver; namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception; use Doctrine\DBAL\Driver\API\PostgreSQL;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion; use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion;
use Doctrine\DBAL\Platforms\PostgreSQL100Platform; use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
...@@ -17,70 +16,13 @@ use Doctrine\DBAL\Schema\PostgreSqlSchemaManager; ...@@ -17,70 +16,13 @@ use Doctrine\DBAL\Schema\PostgreSqlSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver; use Doctrine\DBAL\VersionAwarePlatformDriver;
use function preg_match; use function preg_match;
use function strpos;
use function version_compare; 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 public function createDatabasePlatformForVersion(string $version): AbstractPlatform
{ {
if (preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts) === 0) { 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 ...@@ -107,8 +49,13 @@ abstract class AbstractPostgreSQLDriver implements ExceptionConverterDriver, Ver
return new PostgreSQL94Platform(); 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; ...@@ -6,13 +6,15 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; 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\AbstractPlatform;
use Doctrine\DBAL\Platforms\SQLServer2012Platform; use Doctrine\DBAL\Platforms\SQLServer2012Platform;
use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SQLServerSchemaManager; 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 abstract class AbstractSQLServerDriver implements Driver
{ {
...@@ -21,8 +23,13 @@ abstract class AbstractSQLServerDriver implements Driver ...@@ -21,8 +23,13 @@ abstract class AbstractSQLServerDriver implements Driver
return new SQLServer2012Platform(); 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 @@ ...@@ -2,16 +2,16 @@
declare(strict_types=1); 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 * @internal
* *
* @psalm-immutable * @psalm-immutable
*/ */
final class PortWithoutHost extends AbstractDriverException final class PortWithoutHost extends AbstractException
{ {
public static function new(): self public static function new(): self
{ {
......
...@@ -6,90 +6,30 @@ namespace Doctrine\DBAL\Driver; ...@@ -6,90 +6,30 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface; use Doctrine\DBAL\Driver\API\ExceptionConverter;
use Doctrine\DBAL\Exception; use Doctrine\DBAL\Driver\API\SQLite;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SqliteSchemaManager; 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
{ {
/** public function getDatabasePlatform(): AbstractPlatform
* {@inheritdoc}
*
* @link http://www.sqlite.org/c3ref/c_abort.html
*/
public function convertException(string $message, DriverExceptionInterface $exception): DriverException
{ {
if (strpos($exception->getMessage(), 'database is locked') !== false) { return new SqlitePlatform();
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);
} }
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); ...@@ -4,25 +4,23 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver; namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\DBALException;
/** /**
* Connection interface. * Connection interface.
* Driver connections must implement this interface. * Driver connections must implement this interface.
*
* This resembles (a subset of) the PDO interface.
*/ */
interface Connection interface Connection
{ {
/** /**
* Prepares a statement for execution and returns a Statement object. * Prepares a statement for execution and returns a Statement object.
*
* @throws Exception
*/ */
public function prepare(string $sql): Statement; public function prepare(string $sql): Statement;
/** /**
* Executes an SQL statement, returning a result set as a Statement object. * Executes an SQL statement, returning a result set as a Statement object.
* *
* @throws DBALException * @throws Exception
*/ */
public function query(string $sql): Result; public function query(string $sql): Result;
...@@ -34,33 +32,35 @@ interface Connection ...@@ -34,33 +32,35 @@ interface Connection
/** /**
* Executes an SQL statement and return the number of affected rows. * 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. * Returns the ID of the last inserted row or sequence value.
*
* @throws Exception
*/ */
public function lastInsertId(?string $name = null): string; public function lastInsertId(?string $name = null): string;
/** /**
* Initiates a transaction. * Initiates a transaction.
* *
* @throws DriverException * @throws Exception
*/ */
public function beginTransaction(): void; public function beginTransaction(): void;
/** /**
* Commits a transaction. * Commits a transaction.
* *
* @throws DriverException * @throws Exception
*/ */
public function commit(): void; public function commit(): void;
/** /**
* Rolls back the current transaction, as initiated by beginTransaction(). * Rolls back the current transaction, as initiated by beginTransaction().
* *
* @throws DriverException * @throws Exception
*/ */
public function rollBack(): void; public function rollBack(): void;
} }
...@@ -14,7 +14,7 @@ use Throwable; ...@@ -14,7 +14,7 @@ use Throwable;
* *
* @psalm-immutable * @psalm-immutable
*/ */
interface DriverException extends Throwable interface Exception extends Throwable
{ {
/** /**
* Returns the SQLSTATE the driver was in at the time the error occurred. * Returns the SQLSTATE the driver was in at the time the error occurred.
......
...@@ -2,16 +2,16 @@ ...@@ -2,16 +2,16 @@
declare(strict_types=1); 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 * @internal
* *
* @psalm-immutable * @psalm-immutable
*/ */
final class IdentityColumnsNotSupported extends OCI8Exception final class IdentityColumnsNotSupported extends AbstractException
{ {
public static function new(): self 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 @@ ...@@ -2,22 +2,24 @@
declare(strict_types=1); 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; use function sprintf;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class UnknownType extends MysqliException final class UnknownParameterType extends AbstractException
{ {
/** /**
* @param mixed $type * @param mixed $type
*/ */
public static function new($type): self 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 ...@@ -12,7 +12,7 @@ final class FetchUtils
/** /**
* @return mixed|false * @return mixed|false
* *
* @throws DriverException * @throws Exception
*/ */
public static function fetchOne(Result $result) public static function fetchOne(Result $result)
{ {
...@@ -28,7 +28,7 @@ final class FetchUtils ...@@ -28,7 +28,7 @@ final class FetchUtils
/** /**
* @return array<int,array<int,mixed>> * @return array<int,array<int,mixed>>
* *
* @throws DriverException * @throws Exception
*/ */
public static function fetchAllNumeric(Result $result): array public static function fetchAllNumeric(Result $result): array
{ {
...@@ -44,7 +44,7 @@ final class FetchUtils ...@@ -44,7 +44,7 @@ final class FetchUtils
/** /**
* @return array<int,array<string,mixed>> * @return array<int,array<string,mixed>>
* *
* @throws DriverException * @throws Exception
*/ */
public static function fetchAllAssociative(Result $result): array public static function fetchAllAssociative(Result $result): array
{ {
...@@ -60,7 +60,7 @@ final class FetchUtils ...@@ -60,7 +60,7 @@ final class FetchUtils
/** /**
* @return array<int,mixed> * @return array<int,mixed>
* *
* @throws DriverException * @throws Exception
*/ */
public static function fetchFirstColumn(Result $result): array public static function fetchFirstColumn(Result $result): array
{ {
......
<?php <?php
declare(strict_types=0); declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError; use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed; use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed; use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
...@@ -29,15 +30,17 @@ use function error_get_last; ...@@ -29,15 +30,17 @@ use function error_get_last;
use const DB2_AUTOCOMMIT_OFF; use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON; use const DB2_AUTOCOMMIT_ON;
final class DB2Connection implements ServerInfoAwareConnection final class Connection implements ServerInfoAwareConnection
{ {
/** @var resource */ /** @var resource */
private $conn; private $conn;
/** /**
* @internal The connection can be only instantiated by its driver.
*
* @param array<string,mixed> $driverOptions * @param array<string,mixed> $driverOptions
* *
* @throws DB2Exception * @throws Exception
*/ */
public function __construct( public function __construct(
string $database, string $database,
...@@ -75,7 +78,7 @@ final class DB2Connection implements ServerInfoAwareConnection ...@@ -75,7 +78,7 @@ final class DB2Connection implements ServerInfoAwareConnection
throw PrepareFailed::new(error_get_last()['message']); throw PrepareFailed::new(error_get_last()['message']);
} }
return new DB2Statement($stmt); return new Statement($stmt);
} }
public function query(string $sql): ResultInterface public function query(string $sql): ResultInterface
...@@ -88,9 +91,9 @@ final class DB2Connection implements ServerInfoAwareConnection ...@@ -88,9 +91,9 @@ final class DB2Connection implements ServerInfoAwareConnection
return "'" . db2_escape_string($input) . "'"; 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) { if ($stmt === false) {
throw ConnectionError::new($this->conn); throw ConnectionError::new($this->conn);
......
...@@ -5,19 +5,16 @@ declare(strict_types=1); ...@@ -5,19 +5,16 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\AbstractDB2Driver; use Doctrine\DBAL\Driver\AbstractDB2Driver;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
/** final class Driver extends AbstractDB2Driver
* IBM DB2 Driver.
*/
final class DB2Driver extends AbstractDB2Driver
{ {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function connect(array $params): Connection public function connect(array $params): ConnectionInterface
{ {
return new DB2Connection( return new Connection(
DataSourceName::fromConnectionParameters($params)->toString(), DataSourceName::fromConnectionParameters($params)->toString(),
isset($params['persistent']) && $params['persistent'] === true, isset($params['persistent']) && $params['persistent'] === true,
$params['user'] ?? '', $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); ...@@ -4,15 +4,17 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception; 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_error;
use function db2_conn_errormsg; use function db2_conn_errormsg;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class ConnectionError extends DB2Exception final class ConnectionError extends AbstractException
{ {
/** /**
* @param resource $connection * @param resource $connection
......
...@@ -4,15 +4,17 @@ declare(strict_types=1); ...@@ -4,15 +4,17 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception; 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_error;
use function db2_conn_errormsg; use function db2_conn_errormsg;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class ConnectionFailed extends DB2Exception final class ConnectionFailed extends AbstractException
{ {
public static function new(): self public static function new(): self
{ {
......
...@@ -4,12 +4,14 @@ declare(strict_types=1); ...@@ -4,12 +4,14 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception; namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception; use Doctrine\DBAL\Driver\AbstractException;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class PrepareFailed extends DB2Exception final class PrepareFailed extends AbstractException
{ {
public static function new(string $message): self public static function new(string $message): self
{ {
......
...@@ -4,15 +4,17 @@ declare(strict_types=1); ...@@ -4,15 +4,17 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception; 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_error;
use function db2_stmt_errormsg; use function db2_stmt_errormsg;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class StatementError extends DB2Exception final class StatementError extends AbstractException
{ {
/** /**
* @param resource $statement * @param resource $statement
......
...@@ -5,6 +5,7 @@ declare(strict_types=1); ...@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\FetchUtils; use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Result as ResultInterface;
use function db2_fetch_array; use function db2_fetch_array;
...@@ -13,7 +14,6 @@ use function db2_free_result; ...@@ -13,7 +14,6 @@ use function db2_free_result;
use function db2_num_fields; use function db2_num_fields;
use function db2_num_rows; use function db2_num_rows;
use function db2_stmt_error; use function db2_stmt_error;
use function db2_stmt_errormsg;
final class Result implements ResultInterface final class Result implements ResultInterface
{ {
...@@ -21,6 +21,8 @@ final class Result implements ResultInterface ...@@ -21,6 +21,8 @@ final class Result implements ResultInterface
private $statement; private $statement;
/** /**
* @internal The result can be only instantiated by its driver connection or statement.
*
* @param resource $statement * @param resource $statement
*/ */
public function __construct($statement) public function __construct($statement)
...@@ -36,7 +38,7 @@ final class Result implements ResultInterface ...@@ -36,7 +38,7 @@ final class Result implements ResultInterface
$row = @db2_fetch_array($this->statement); $row = @db2_fetch_array($this->statement);
if ($row === false && db2_stmt_error($this->statement) !== '02000') { if ($row === false && db2_stmt_error($this->statement) !== '02000') {
throw new DB2Exception(db2_stmt_errormsg($this->statement)); throw StatementError::new($this->statement);
} }
return $row; return $row;
...@@ -50,7 +52,7 @@ final class Result implements ResultInterface ...@@ -50,7 +52,7 @@ final class Result implements ResultInterface
$row = @db2_fetch_assoc($this->statement); $row = @db2_fetch_assoc($this->statement);
if ($row === false && db2_stmt_error($this->statement) !== '02000') { if ($row === false && db2_stmt_error($this->statement) !== '02000') {
throw new DB2Exception(db2_stmt_errormsg($this->statement)); throw StatementError::new($this->statement);
} }
return $row; return $row;
......
...@@ -4,9 +4,13 @@ declare(strict_types=1); ...@@ -4,9 +4,13 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2; 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\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use function assert; use function assert;
...@@ -28,7 +32,7 @@ use const DB2_LONG; ...@@ -28,7 +32,7 @@ use const DB2_LONG;
use const DB2_PARAM_FILE; use const DB2_PARAM_FILE;
use const DB2_PARAM_IN; use const DB2_PARAM_IN;
final class DB2Statement implements Statement final class Statement implements StatementInterface
{ {
/** @var resource */ /** @var resource */
private $stmt; private $stmt;
...@@ -45,6 +49,8 @@ final class DB2Statement implements Statement ...@@ -45,6 +49,8 @@ final class DB2Statement implements Statement
private $lobs = []; private $lobs = [];
/** /**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $stmt * @param resource $stmt
*/ */
public function __construct($stmt) public function __construct($stmt)
...@@ -98,7 +104,7 @@ final class DB2Statement implements Statement ...@@ -98,7 +104,7 @@ final class DB2Statement implements Statement
* @param int $position Parameter position * @param int $position Parameter position
* @param mixed $variable * @param mixed $variable
* *
* @throws DB2Exception * @throws Exception
*/ */
private function bind(int $position, &$variable, int $parameterType, int $dataType): void private function bind(int $position, &$variable, int $parameterType, int $dataType): void
{ {
...@@ -152,14 +158,14 @@ final class DB2Statement implements Statement ...@@ -152,14 +158,14 @@ final class DB2Statement implements Statement
/** /**
* @return resource * @return resource
* *
* @throws DB2Exception * @throws Exception
*/ */
private function createTemporaryFile() private function createTemporaryFile()
{ {
$handle = @tmpfile(); $handle = @tmpfile();
if ($handle === false) { if ($handle === false) {
throw new DB2Exception('Could not create temporary file: ' . error_get_last()['message']); throw CannotCreateTemporaryFile::new(error_get_last()['message']);
} }
return $handle; return $handle;
...@@ -169,24 +175,24 @@ final class DB2Statement implements Statement ...@@ -169,24 +175,24 @@ final class DB2Statement implements Statement
* @param resource $source * @param resource $source
* @param resource $target * @param resource $target
* *
* @throws DB2Exception * @throws Exception
*/ */
private function copyStreamToStream($source, $target): void private function copyStreamToStream($source, $target): void
{ {
if (@stream_copy_to_stream($source, $target) === false) { 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 * @param resource $target
* *
* @throws DB2Exception * @throws Exception
*/ */
private function writeStringToStream(string $string, $target): void private function writeStringToStream(string $string, $target): void
{ {
if (@fwrite($target, $string) === false) { 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); ...@@ -4,8 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli; namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError; 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\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\Driver\Statement as DriverStatement;
...@@ -15,7 +16,7 @@ use function floor; ...@@ -15,7 +16,7 @@ use function floor;
use function mysqli_init; use function mysqli_init;
use function stripos; use function stripos;
class MysqliConnection implements PingableConnection, ServerInfoAwareConnection final class Connection implements ServerInfoAwareConnection
{ {
/** /**
* Name of the option to set connection flags * Name of the option to set connection flags
...@@ -26,10 +27,12 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection ...@@ -26,10 +27,12 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
private $conn; private $conn;
/** /**
* @internal The connection can be only instantiated by its driver.
*
* @param iterable<Initializer> $preInitializers * @param iterable<Initializer> $preInitializers
* @param iterable<Initializer> $postInitializers * @param iterable<Initializer> $postInitializers
* *
* @throws MysqliException * @throws Exception
*/ */
public function __construct( public function __construct(
string $host = '', string $host = '',
...@@ -49,11 +52,7 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection ...@@ -49,11 +52,7 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
} }
if (! @$connection->real_connect($host, $username, $password, $database, $port, $socket, $flags)) { if (! @$connection->real_connect($host, $username, $password, $database, $port, $socket, $flags)) {
throw new MysqliException( throw ConnectionFailed::new($connection);
$connection->connect_error,
'HY000',
$connection->connect_errno
);
} }
foreach ($postInitializers as $initializer) { foreach ($postInitializers as $initializer) {
...@@ -97,7 +96,7 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection ...@@ -97,7 +96,7 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
public function prepare(string $sql): DriverStatement public function prepare(string $sql): DriverStatement
{ {
return new MysqliStatement($this->conn, $sql); return new Statement($this->conn, $sql);
} }
public function query(string $sql): ResultInterface public function query(string $sql): ResultInterface
...@@ -110,9 +109,9 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection ...@@ -110,9 +109,9 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
return "'" . $this->conn->escape_string($input) . "'"; 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); throw ConnectionError::new($this->conn);
} }
...@@ -142,16 +141,4 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection ...@@ -142,16 +141,4 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
throw ConnectionError::new($this->conn); 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); ...@@ -5,7 +5,8 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli; namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\AbstractMySQLDriver; 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\Charset;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Options; use Doctrine\DBAL\Driver\Mysqli\Initializer\Options;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure; use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure;
...@@ -17,9 +18,9 @@ final class Driver extends AbstractMySQLDriver ...@@ -17,9 +18,9 @@ final class Driver extends AbstractMySQLDriver
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
* @return MysqliConnection * @return Connection
*/ */
public function connect(array $params): Connection public function connect(array $params): ConnectionInterface
{ {
if (! empty($params['persistent'])) { if (! empty($params['persistent'])) {
if (! isset($params['host'])) { if (! isset($params['host'])) {
...@@ -38,9 +39,9 @@ final class Driver extends AbstractMySQLDriver ...@@ -38,9 +39,9 @@ final class Driver extends AbstractMySQLDriver
if (isset($params['driver_options'])) { if (isset($params['driver_options'])) {
$driverOptions = $params['driver_options']; $driverOptions = $params['driver_options'];
if (isset($driverOptions[MysqliConnection::OPTION_FLAGS])) { if (isset($driverOptions[Connection::OPTION_FLAGS])) {
$flags = $driverOptions[MysqliConnection::OPTION_FLAGS]; $flags = $driverOptions[Connection::OPTION_FLAGS];
unset($driverOptions[MysqliConnection::OPTION_FLAGS]); unset($driverOptions[Connection::OPTION_FLAGS]);
} }
$preInitializers = $this->withOptions($preInitializers, $driverOptions); $preInitializers = $this->withOptions($preInitializers, $driverOptions);
...@@ -49,7 +50,7 @@ final class Driver extends AbstractMySQLDriver ...@@ -49,7 +50,7 @@ final class Driver extends AbstractMySQLDriver
$preInitializers = $this->withSecure($preInitializers, $params); $preInitializers = $this->withSecure($preInitializers, $params);
$postInitializers = $this->withCharset($postInitializers, $params); $postInitializers = $this->withCharset($postInitializers, $params);
return new MysqliConnection( return new Connection(
$host, $host,
$params['user'] ?? '', $params['user'] ?? '',
$params['password'] ?? '', $params['password'] ?? '',
......
...@@ -4,23 +4,18 @@ declare(strict_types=1); ...@@ -4,23 +4,18 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception; namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException; use Doctrine\DBAL\Driver\AbstractException;
use mysqli; use mysqli;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class ConnectionError extends MysqliException final class ConnectionError extends AbstractException
{ {
public static function new(mysqli $connection): self public static function new(mysqli $connection): self
{ {
$connectionSQLState = $connection->sqlstate; return new self($connection->error, $connection->sqlstate, $connection->errno);
$sqlState = null;
if ($connectionSQLState !== false) {
$sqlState = $connectionSQLState;
}
return new self($connection->error, $sqlState, $connection->errno);
} }
} }
...@@ -4,17 +4,18 @@ declare(strict_types=1); ...@@ -4,17 +4,18 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception; namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException; use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
use function sprintf;
/** /**
* @internal
*
* @psalm-immutable * @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); ...@@ -4,17 +4,19 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception; namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException; use Doctrine\DBAL\Driver\AbstractException;
use function sprintf; use function sprintf;
/** /**
* @internal
*
* @psalm-immutable * @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 @@ ...@@ -2,14 +2,16 @@
declare(strict_types=1); declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli; namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/** /**
* @internal * @internal
* *
* @psalm-immutable * @psalm-immutable
*/ */
final class HostRequired extends MysqliException final class HostRequired extends AbstractException
{ {
public static function forPersistentConnection(): self public static function forPersistentConnection(): self
{ {
......
...@@ -4,7 +4,7 @@ declare(strict_types=1); ...@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception; namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException; use Doctrine\DBAL\Driver\AbstractException;
use mysqli; use mysqli;
use function sprintf; use function sprintf;
...@@ -14,7 +14,7 @@ use function sprintf; ...@@ -14,7 +14,7 @@ use function sprintf;
* *
* @psalm-immutable * @psalm-immutable
*/ */
final class InvalidCharset extends MysqliException final class InvalidCharset extends AbstractException
{ {
public static function fromCharset(mysqli $connection, string $charset): self public static function fromCharset(mysqli $connection, string $charset): self
{ {
......
...@@ -4,7 +4,7 @@ declare(strict_types=1); ...@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception; namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException; use Doctrine\DBAL\Driver\AbstractException;
use function sprintf; use function sprintf;
...@@ -13,7 +13,7 @@ use function sprintf; ...@@ -13,7 +13,7 @@ use function sprintf;
* *
* @psalm-immutable * @psalm-immutable
*/ */
final class InvalidOption extends MysqliException final class InvalidOption extends AbstractException
{ {
/** /**
* @param mixed $value * @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); ...@@ -4,13 +4,15 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception; namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException; use Doctrine\DBAL\Driver\AbstractException;
use mysqli_stmt; use mysqli_stmt;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class StatementError extends MysqliException final class StatementError extends AbstractException
{ {
public static function new(mysqli_stmt $statement): self public static function new(mysqli_stmt $statement): self
{ {
......
...@@ -4,12 +4,13 @@ declare(strict_types=1); ...@@ -4,12 +4,13 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli; namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use mysqli; use mysqli;
interface Initializer interface Initializer
{ {
/** /**
* @throws MysqliException * @throws Exception
*/ */
public function initialize(mysqli $connection): void; 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); ...@@ -4,7 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli; namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\FetchUtils; use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Result as ResultInterface;
use mysqli_stmt; use mysqli_stmt;
use stdClass; use stdClass;
...@@ -41,7 +43,9 @@ final class Result implements ResultInterface ...@@ -41,7 +43,9 @@ final class Result implements ResultInterface
private $boundValues = []; 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) public function __construct(mysqli_stmt $statement)
{ {
...@@ -71,7 +75,7 @@ final class Result implements ResultInterface ...@@ -71,7 +75,7 @@ final class Result implements ResultInterface
// Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql, // 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 // 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 // @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 // Make sure that the values are bound after each execution. Otherwise, if free() has been
...@@ -88,7 +92,7 @@ final class Result implements ResultInterface ...@@ -88,7 +92,7 @@ final class Result implements ResultInterface
} }
if (! $this->statement->bind_result(...$refs)) { 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 ...@@ -100,7 +104,7 @@ final class Result implements ResultInterface
$ret = $this->statement->fetch(); $ret = $this->statement->fetch();
if ($ret === false) { if ($ret === false) {
throw new MysqliException($this->statement->error, $this->statement->sqlstate, $this->statement->errno); throw StatementError::new($this->statement);
} }
if ($ret === null) { if ($ret === null) {
......
...@@ -4,14 +4,14 @@ declare(strict_types=1); ...@@ -4,14 +4,14 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli; 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\ConnectionError;
use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset; 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\StatementError;
use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownType;
use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use mysqli; use mysqli;
use mysqli_stmt; use mysqli_stmt;
...@@ -26,7 +26,7 @@ use function is_int; ...@@ -26,7 +26,7 @@ use function is_int;
use function is_resource; use function is_resource;
use function str_repeat; use function str_repeat;
final class MysqliStatement implements Statement final class Statement implements StatementInterface
{ {
/** @var string[] */ /** @var string[] */
private static $paramTypeMap = [ private static $paramTypeMap = [
...@@ -58,7 +58,9 @@ final class MysqliStatement implements Statement ...@@ -58,7 +58,9 @@ final class MysqliStatement implements Statement
private $values = []; private $values = [];
/** /**
* @throws MysqliException * @internal The statement can be only instantiated by its driver connection.
*
* @throws Exception
*/ */
public function __construct(mysqli $conn, string $sql) public function __construct(mysqli $conn, string $sql)
{ {
...@@ -89,7 +91,7 @@ final class MysqliStatement implements Statement ...@@ -89,7 +91,7 @@ final class MysqliStatement implements Statement
assert(is_int($param)); assert(is_int($param));
if (! isset(self::$paramTypeMap[$type])) { if (! isset(self::$paramTypeMap[$type])) {
throw UnknownType::new($type); throw UnknownParameterType::new($type);
} }
$this->boundValues[$param] =& $variable; $this->boundValues[$param] =& $variable;
...@@ -104,7 +106,7 @@ final class MysqliStatement implements Statement ...@@ -104,7 +106,7 @@ final class MysqliStatement implements Statement
assert(is_int($param)); assert(is_int($param));
if (! isset(self::$paramTypeMap[$type])) { if (! isset(self::$paramTypeMap[$type])) {
throw UnknownType::new($type); throw UnknownParameterType::new($type);
} }
$this->values[$param] = $value; $this->values[$param] = $value;
...@@ -135,7 +137,7 @@ final class MysqliStatement implements Statement ...@@ -135,7 +137,7 @@ final class MysqliStatement implements Statement
/** /**
* Binds parameters with known types previously bound to the statement * Binds parameters with known types previously bound to the statement
* *
* @throws DriverException * @throws Exception
*/ */
private function bindTypedParameters(): void private function bindTypedParameters(): void
{ {
...@@ -151,7 +153,7 @@ final class MysqliStatement implements Statement ...@@ -151,7 +153,7 @@ final class MysqliStatement implements Statement
if ($types[$parameter - 1] === self::$paramTypeMap[ParameterType::LARGE_OBJECT]) { if ($types[$parameter - 1] === self::$paramTypeMap[ParameterType::LARGE_OBJECT]) {
if (is_resource($value)) { if (is_resource($value)) {
if (get_resource_type($value) !== 'stream') { 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; $streams[$parameter] = $value;
...@@ -177,7 +179,7 @@ final class MysqliStatement implements Statement ...@@ -177,7 +179,7 @@ final class MysqliStatement implements Statement
* *
* @param array<int, resource> $streams * @param array<int, resource> $streams
* *
* @throws MysqliException * @throws Exception
*/ */
private function sendLongData(array $streams): void private function sendLongData(array $streams): void
{ {
......
...@@ -4,29 +4,29 @@ declare(strict_types=1); ...@@ -4,29 +4,29 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8; 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\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\Driver\Statement as DriverStatement;
use UnexpectedValueException;
use function addcslashes; use function addcslashes;
use function assert;
use function oci_commit; use function oci_commit;
use function oci_connect; use function oci_connect;
use function oci_error;
use function oci_pconnect; use function oci_pconnect;
use function oci_rollback; use function oci_rollback;
use function oci_server_version; use function oci_server_version;
use function preg_match; use function preg_match;
use function sprintf;
use function str_replace; use function str_replace;
use const OCI_NO_AUTO_COMMIT; use const OCI_NO_AUTO_COMMIT;
/** final class Connection implements ConnectionInterface, ServerInfoAwareConnection
* OCI8 implementation of the Connection interface.
*/
final class OCI8Connection implements Connection, ServerInfoAwareConnection
{ {
/** @var resource */ /** @var resource */
private $connection; private $connection;
...@@ -37,7 +37,9 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -37,7 +37,9 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
/** /**
* Creates a Connection to an Oracle Database using oci8 extension. * 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( public function __construct(
string $username, string $username,
...@@ -54,43 +56,30 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -54,43 +56,30 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
} }
if ($connection === false) { if ($connection === false) {
throw OCI8Exception::fromErrorInfo(oci_error()); throw ConnectionFailed::new();
} }
$this->connection = $connection; $this->connection = $connection;
$this->executionMode = new ExecutionMode(); $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 public function getServerVersion(): string
{ {
$version = oci_server_version($this->connection); $version = oci_server_version($this->connection);
if ($version === false) { 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) { assert(preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches) === 1);
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
)
);
}
return $matches[1]; return $matches[1];
} }
public function prepare(string $sql): DriverStatement 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 public function query(string $sql): ResultInterface
...@@ -103,9 +92,9 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -103,9 +92,9 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
return "'" . addcslashes(str_replace("'", "''", $input), "\000\n\r\\\032") . "'"; 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 public function lastInsertId(?string $name = null): string
...@@ -117,7 +106,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -117,7 +106,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
$result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne(); $result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne();
if ($result === false) { if ($result === false) {
throw new OCI8Exception('lastInsertId failed: Query was executed but no result was returned.'); throw SequenceDoesNotExist::new();
} }
return $result; return $result;
...@@ -131,7 +120,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -131,7 +120,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
public function commit(): void public function commit(): void
{ {
if (! oci_commit($this->connection)) { if (! oci_commit($this->connection)) {
throw OCI8Exception::fromErrorInfo(oci_error($this->connection)); throw Error::new($this->connection);
} }
$this->executionMode->enableAutoCommit(); $this->executionMode->enableAutoCommit();
...@@ -140,7 +129,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection ...@@ -140,7 +129,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
public function rollBack(): void public function rollBack(): void
{ {
if (! oci_rollback($this->connection)) { if (! oci_rollback($this->connection)) {
throw OCI8Exception::fromErrorInfo(oci_error($this->connection)); throw Error::new($this->connection);
} }
$this->executionMode->enableAutoCommit(); $this->executionMode->enableAutoCommit();
......
...@@ -4,6 +4,9 @@ declare(strict_types=1); ...@@ -4,6 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8; namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\OCI8\Exception\NonTerminatedStringLiteral;
use function count; use function count;
use function implode; use function implode;
use function preg_match; use function preg_match;
...@@ -29,7 +32,7 @@ final class ConvertPositionalToNamedPlaceholders ...@@ -29,7 +32,7 @@ final class ConvertPositionalToNamedPlaceholders
* *
* @return mixed[] [0] => the statement value (string), [1] => the paramMap value (array). * @return mixed[] [0] => the statement value (string), [1] => the paramMap value (array).
* *
* @throws OCI8Exception * @throws Exception
*/ */
public function __invoke(string $statement): array public function __invoke(string $statement): array
{ {
...@@ -52,7 +55,7 @@ final class ConvertPositionalToNamedPlaceholders ...@@ -52,7 +55,7 @@ final class ConvertPositionalToNamedPlaceholders
} }
} while ($result); } while ($result);
if ($currentLiteralDelimiter) { if ($currentLiteralDelimiter !== null) {
throw NonTerminatedStringLiteral::new($tokenOffset - 1); throw NonTerminatedStringLiteral::new($tokenOffset - 1);
} }
......
...@@ -5,7 +5,7 @@ declare(strict_types=1); ...@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8; namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\AbstractOracleDriver; use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use const OCI_NO_AUTO_COMMIT; use const OCI_NO_AUTO_COMMIT;
...@@ -17,9 +17,9 @@ final class Driver extends AbstractOracleDriver ...@@ -17,9 +17,9 @@ final class Driver extends AbstractOracleDriver
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function connect(array $params): Connection public function connect(array $params): ConnectionInterface
{ {
return new OCI8Connection( return new Connection(
$params['user'] ?? '', $params['user'] ?? '',
$params['password'] ?? '', $params['password'] ?? '',
$this->constructDsn($params), $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 @@ ...@@ -2,14 +2,18 @@
declare(strict_types=1); declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8; namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function sprintf; use function sprintf;
/** /**
* @internal
*
* @psalm-immutable * @psalm-immutable
*/ */
final class NonTerminatedStringLiteral extends OCI8Exception final class NonTerminatedStringLiteral extends AbstractException
{ {
public static function new(int $offset): self 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 ...@@ -26,6 +26,8 @@ final class Result implements ResultInterface
private $statement; private $statement;
/** /**
* @internal The result can be only instantiated by its driver connection or statement.
*
* @param resource $statement * @param resource $statement
*/ */
public function __construct($statement) public function __construct($statement)
......
...@@ -4,19 +4,20 @@ declare(strict_types=1); ...@@ -4,19 +4,20 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8; 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\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use function assert; use function assert;
use function is_int; use function is_int;
use function is_resource; use function is_resource;
use function oci_bind_by_name; use function oci_bind_by_name;
use function oci_error;
use function oci_execute; use function oci_execute;
use function oci_new_descriptor; use function oci_new_descriptor;
use function oci_parse; use function oci_parse;
use function sprintf;
use const OCI_B_BIN; use const OCI_B_BIN;
use const OCI_B_BLOB; use const OCI_B_BLOB;
...@@ -26,10 +27,7 @@ use const OCI_NO_AUTO_COMMIT; ...@@ -26,10 +27,7 @@ use const OCI_NO_AUTO_COMMIT;
use const OCI_TEMP_BLOB; use const OCI_TEMP_BLOB;
use const SQLT_CHR; use const SQLT_CHR;
/** final class Statement implements StatementInterface
* The OCI8 implementation of the Statement interface.
*/
final class OCI8Statement implements Statement
{ {
/** @var resource */ /** @var resource */
private $connection; private $connection;
...@@ -55,10 +53,12 @@ final class OCI8Statement implements Statement ...@@ -55,10 +53,12 @@ final class OCI8Statement implements Statement
/** /**
* Creates a new OCI8Statement that uses the given connection handle and SQL 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 resource $dbh The connection handle.
* @param string $query The SQL query. * @param string $query The SQL query.
* *
* @throws OCI8Exception * @throws Exception
*/ */
public function __construct($dbh, string $query, ExecutionMode $executionMode) public function __construct($dbh, string $query, ExecutionMode $executionMode)
{ {
...@@ -88,7 +88,7 @@ final class OCI8Statement implements Statement ...@@ -88,7 +88,7 @@ final class OCI8Statement implements Statement
{ {
if (is_int($param)) { if (is_int($param)) {
if (! isset($this->parameterMap[$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]; $param = $this->parameterMap[$param];
...@@ -116,7 +116,7 @@ final class OCI8Statement implements Statement ...@@ -116,7 +116,7 @@ final class OCI8Statement implements Statement
$this->convertParameterType($type) $this->convertParameterType($type)
) )
) { ) {
throw OCI8Exception::fromErrorInfo(oci_error($this->statement)); throw Error::new($this->statement);
} }
} }
...@@ -162,7 +162,7 @@ final class OCI8Statement implements Statement ...@@ -162,7 +162,7 @@ final class OCI8Statement implements Statement
$ret = @oci_execute($this->statement, $mode); $ret = @oci_execute($this->statement, $mode);
if (! $ret) { if (! $ret) {
throw OCI8Exception::fromErrorInfo(oci_error($this->statement)); throw Error::new($this->statement);
} }
return new Result($this->statement); return new Result($this->statement);
......
...@@ -2,49 +2,50 @@ ...@@ -2,49 +2,50 @@
declare(strict_types=1); 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\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use PDO; use PDO;
use PDOException;
use PDOStatement;
use function assert; use function assert;
/** final class Connection implements ServerInfoAwareConnection
* PDO implementation of the Connection interface.
*
* Used by all PDO-based drivers.
*/
class PDOConnection implements ServerInfoAwareConnection
{ {
/** @var PDO */ /** @var PDO */
private $connection; private $connection;
/** /**
* @internal The connection can be only instantiated by its driver.
*
* @param array<int, mixed> $options * @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 = []) public function __construct(string $dsn, string $username = '', string $password = '', array $options = [])
{ {
try { try {
$this->connection = new PDO($dsn, $username, $password, $options); $this->connection = new PDO($dsn, $username, $password, $options);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
public function exec(string $statement): int public function exec(string $sql): int
{ {
try { try {
$result = $this->connection->exec($statement); $result = $this->connection->exec($sql);
assert($result !== false); assert($result !== false);
return $result; return $result;
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
...@@ -53,14 +54,19 @@ class PDOConnection implements ServerInfoAwareConnection ...@@ -53,14 +54,19 @@ class PDOConnection implements ServerInfoAwareConnection
return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION);
} }
public function prepare(string $sql): Statement /**
* {@inheritDoc}
*
* @return Statement
*/
public function prepare(string $sql): StatementInterface
{ {
try { try {
return $this->createStatement( return $this->createStatement(
$this->connection->prepare($sql) $this->connection->prepare($sql)
); );
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
...@@ -68,11 +74,11 @@ class PDOConnection implements ServerInfoAwareConnection ...@@ -68,11 +74,11 @@ class PDOConnection implements ServerInfoAwareConnection
{ {
try { try {
$stmt = $this->connection->query($sql); $stmt = $this->connection->query($sql);
assert($stmt instanceof \PDOStatement); assert($stmt instanceof PDOStatement);
return new Result($stmt); return new Result($stmt);
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
...@@ -89,17 +95,17 @@ class PDOConnection implements ServerInfoAwareConnection ...@@ -89,17 +95,17 @@ class PDOConnection implements ServerInfoAwareConnection
} }
return $this->connection->lastInsertId($name); return $this->connection->lastInsertId($name);
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
/** /**
* Creates a wrapped statement * 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 public function beginTransaction(): void
......
...@@ -2,19 +2,19 @@ ...@@ -2,19 +2,19 @@
declare(strict_types=1); 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 * @psalm-immutable
*/ */
class PDOException extends AbstractDriverException final class Exception extends AbstractException
{ {
/** public static function new(PDOException $exception): self
* @param \PDOException $exception The PDO exception to wrap.
*/
public function __construct(\PDOException $exception)
{ {
if ($exception->errorInfo !== null) { if ($exception->errorInfo !== null) {
[$sqlState, $code] = $exception->errorInfo; [$sqlState, $code] = $exception->errorInfo;
...@@ -23,6 +23,6 @@ class PDOException extends AbstractDriverException ...@@ -23,6 +23,6 @@ class PDOException extends AbstractDriverException
$sqlState = null; $sqlState = null;
} }
parent::__construct($exception->getMessage(), $sqlState, $code, $exception); return new self($exception->getMessage(), $sqlState, $code, $exception);
} }
} }
...@@ -2,22 +2,19 @@ ...@@ -2,22 +2,19 @@
declare(strict_types=1); declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOMySql; namespace Doctrine\DBAL\Driver\PDO\MySQL;
use Doctrine\DBAL\Driver\AbstractMySQLDriver; use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDOConnection; use Doctrine\DBAL\Driver\PDO\Connection;
use PDO; use PDO;
/**
* PDO MySql driver.
*/
final class Driver extends AbstractMySQLDriver final class Driver extends AbstractMySQLDriver
{ {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function connect(array $params): Connection public function connect(array $params): ConnectionInterface
{ {
$driverOptions = $params['driver_options'] ?? []; $driverOptions = $params['driver_options'] ?? [];
...@@ -25,7 +22,7 @@ final class Driver extends AbstractMySQLDriver ...@@ -25,7 +22,7 @@ final class Driver extends AbstractMySQLDriver
$driverOptions[PDO::ATTR_PERSISTENT] = true; $driverOptions[PDO::ATTR_PERSISTENT] = true;
} }
return new PDOConnection( return new Connection(
$this->constructPdoDsn($params), $this->constructPdoDsn($params),
$params['user'] ?? '', $params['user'] ?? '',
$params['password'] ?? '', $params['password'] ?? '',
......
...@@ -2,27 +2,19 @@ ...@@ -2,27 +2,19 @@
declare(strict_types=1); declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOOracle; namespace Doctrine\DBAL\Driver\PDO\OCI;
use Doctrine\DBAL\Driver\AbstractOracleDriver; use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDOConnection; use Doctrine\DBAL\Driver\PDO\Connection;
use PDO; 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 final class Driver extends AbstractOracleDriver
{ {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function connect(array $params): Connection public function connect(array $params): ConnectionInterface
{ {
$driverOptions = $params['driver_options'] ?? []; $driverOptions = $params['driver_options'] ?? [];
...@@ -30,7 +22,7 @@ final class Driver extends AbstractOracleDriver ...@@ -30,7 +22,7 @@ final class Driver extends AbstractOracleDriver
$driverOptions[PDO::ATTR_PERSISTENT] = true; $driverOptions[PDO::ATTR_PERSISTENT] = true;
} }
return new PDOConnection( return new Connection(
$this->constructPdoDsn($params), $this->constructPdoDsn($params),
$params['user'] ?? '', $params['user'] ?? '',
$params['password'] ?? '', $params['password'] ?? '',
......
...@@ -2,24 +2,21 @@ ...@@ -2,24 +2,21 @@
declare(strict_types=1); declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOPgSql; namespace Doctrine\DBAL\Driver\PDO\PgSQL;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDOConnection; use Doctrine\DBAL\Driver\PDO\Connection;
use PDO; use PDO;
use function defined; use function defined;
/**
* Driver that connects through pdo_pgsql.
*/
final class Driver extends AbstractPostgreSQLDriver final class Driver extends AbstractPostgreSQLDriver
{ {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function connect(array $params): Connection public function connect(array $params): ConnectionInterface
{ {
$driverOptions = $params['driver_options'] ?? []; $driverOptions = $params['driver_options'] ?? [];
...@@ -27,7 +24,7 @@ final class Driver extends AbstractPostgreSQLDriver ...@@ -27,7 +24,7 @@ final class Driver extends AbstractPostgreSQLDriver
$driverOptions[PDO::ATTR_PERSISTENT] = true; $driverOptions[PDO::ATTR_PERSISTENT] = true;
} }
$connection = new PDOConnection( $connection = new Connection(
$this->constructPdoDsn($params), $this->constructPdoDsn($params),
$params['user'] ?? '', $params['user'] ?? '',
$params['password'] ?? '', $params['password'] ?? '',
......
...@@ -4,9 +4,9 @@ declare(strict_types=1); ...@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO; namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\PDOException;
use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Result as ResultInterface;
use PDO; use PDO;
use PDOException;
use PDOStatement; use PDOStatement;
use function assert; use function assert;
...@@ -17,6 +17,9 @@ final class Result implements ResultInterface ...@@ -17,6 +17,9 @@ final class Result implements ResultInterface
/** @var PDOStatement */ /** @var PDOStatement */
private $statement; private $statement;
/**
* @internal The result can be only instantiated by its driver connection or statement.
*/
public function __construct(PDOStatement $statement) public function __construct(PDOStatement $statement)
{ {
$this->statement = $statement; $this->statement = $statement;
...@@ -74,8 +77,8 @@ final class Result implements ResultInterface ...@@ -74,8 +77,8 @@ final class Result implements ResultInterface
{ {
try { try {
return $this->statement->rowCount(); return $this->statement->rowCount();
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
...@@ -83,45 +86,41 @@ final class Result implements ResultInterface ...@@ -83,45 +86,41 @@ final class Result implements ResultInterface
{ {
try { try {
return $this->statement->columnCount(); return $this->statement->columnCount();
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
public function free(): void public function free(): void
{ {
try { $this->statement->closeCursor();
$this->statement->closeCursor();
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
} }
/** /**
* @return mixed|false * @return mixed|false
* *
* @throws PDOException * @throws Exception
*/ */
private function fetch(int $mode) private function fetch(int $mode)
{ {
try { try {
return $this->statement->fetch($mode); return $this->statement->fetch($mode);
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
} }
/** /**
* @return array<int,mixed> * @return array<int,mixed>
* *
* @throws PDOException * @throws Exception
*/ */
private function fetchAll(int $mode): array private function fetchAll(int $mode): array
{ {
try { try {
$data = $this->statement->fetchAll($mode); $data = $this->statement->fetchAll($mode);
} catch (\PDOException $exception) { } catch (PDOException $exception) {
throw new PDOException($exception); throw Exception::new($exception);
} }
assert(is_array($data)); 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