Unverified Commit 92c6ec99 authored by Sergei Morozov's avatar Sergei Morozov

Merge branch '3.0.x'

parents 2b63c38c 4509f271
......@@ -260,6 +260,73 @@ The Doctrine\DBAL\Version class is no longer available: please refrain from chec
# Upgrade to 3.0
## BC BREAK: Changes in `OracleSchemaManager::createDatabase()`
The `$database` argument is no longer nullable or optional.
## BC BREAK: `Doctrine\DBAL\Types\Type::__toString()` removed
Relying on string representation was discouraged and has been removed.
## BC BREAK: Changes in the `Doctrine\DBAL\Schema` API
- Removed unused method `Doctrine\DBAL\Schema\AbstractSchemaManager::_getPortableFunctionsList()`
- Removed unused method `Doctrine\DBAL\Schema\AbstractSchemaManager::_getPortableFunctionDefinition()`
- Removed unused method `Doctrine\DBAL\Schema\OracleSchemaManager::_getPortableFunctionDefinition()`
- Removed unused method `Doctrine\DBAL\Schema\SqliteSchemaManager::_getPortableTableIndexDefinition()`
## BC BREAK: Removed support for DB-generated UUIDs
The support for DB-generated UUIDs was removed as non-portable.
Please generate UUIDs on the application side (e.g. using [ramsey/uuid](https://packagist.org/packages/ramsey/uuid)).
## BC BREAK: Changes in the `Doctrine\DBAL\Connection` API
- The following methods have been removed as leaking internal implementation details: `::getHost()`, `::getPort()`, `::getUsername()`, `::getPassword()`.
## BC BREAK: Changes in the `Doctrine\DBAL\Event` API
- `ConnectionEventArgs::getDriver()`, `::getDatabasePlatform()` and `::getSchemaManager()` methods have been removed. The connection information can be obtained from the connection which is available via `::getConnection()`.
- `SchemaColumnDefinitionEventArgs::getDatabasePlatform()` and `SchemaIndexDefinitionEventArgs::getDatabasePlatform()` have been removed for the same reason as above.
## BC BREAK: Changes in obtaining the currently selected database name
- The `Doctrine\DBAL\Driver::getDatabase()` method has been removed. Please use `Doctrine\DBAL\Connection::getDatabase()` instead.
- `Doctrine\DBAL\Connection::getDatabase()` will always return the name of the database currently connected to, regardless of the configuration parameters and will initialize a database connection if it's not yet established.
- A call to `Doctrine\DBAL\Connection::getDatabase()`, when connected to an SQLite database, will no longer return the database file path.
## BC BREAK: `Doctrine\DBAL\Driver::getName()` removed
The `Doctrine\DBAL\Driver::getName()` has been removed.
## BC BREAK Removed previously deprecated features
* Removed `json_array` type and all associated hacks.
* Removed `Connection::TRANSACTION_*` constants.
* Removed `AbstractPlatform::DATE_INTERVAL_UNIT_*` and `AbstractPlatform::TRIM_*` constants.
* Removed `MysqlSessionInit` listener.
* Removed `MysqlPlatform::getCollationFieldDeclaration()`.
* Removed `AbstractPlatform::getIdentityColumnNullInsertSQL()`.
* Removed `Table::addUnnamedForeignKeyConstraint()` and `Table::addNamedForeignKeyConstraint()`.
* Removed `Table::renameColumn()`.
* Removed `SQLParserUtils::getPlaceholderPositions()`.
* Removed `LoggerChain::addLogger`.
* Removed `AbstractSchemaManager::getFilterSchemaAssetsExpression()`, `Configuration::getFilterSchemaAssetsExpression()`
and `Configuration::getFilterSchemaAssetsExpression()`.
* `SQLParserUtils::*_TOKEN` constants made private.
## BC BREAK changes the `Driver::connect()` signature
The method no longer accepts the `$username`, `$password` and `$driverOptions` arguments. The corresponding values are expected to be passed as the "user", "password" and "driver_options" keys of the `$params` argument respectively.
## Removed `MasterSlaveConnection`
This class was deprecated in favor of `PrimaryReadReplicaConnection`
## Removed `Portability\Connection::PORTABILITY_{PLATFORM}` constants`
The platform-specific portability constants were internal implementation details which are longer relevant.
## BC BREAK changes in fetching statement results
1. The `Statement` interface no longer extends `ResultStatement`.
......@@ -309,19 +376,9 @@ In order to fetch a column with an index other than `0`, use `FetchMode::NUMERIC
`EchoSQLLogger` is no longer available as part of the package.
## BC BREAK: Removed support for SQL Anywhere 12 and older
## BC BREAK: Removed support for SQL Anywhere
DBAL now requires SQL Anywhere 16 or newer, support for unmaintained versions has been dropped.
If you are using any of the legacy versions, you have to upgrade to a newer SQL Anywhere version (16+).
The following classes have been removed:
* `Doctrine\DBAL\Platforms\SQLAnywherePlatform`
* `Doctrine\DBAL\Platforms\SQLAnywhere11Platform`
* `Doctrine\DBAL\Platforms\SQLAnywhere12Platform`
* `Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords`
* `Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords`
* `Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords`
The support for the SQL Anywhere database platform and the corresponding driver has been removed.
## BC BREAK: Removed support for PostgreSQL 9.3 and older
......@@ -435,6 +492,57 @@ Please use other database client applications for import, e.g.:
# Upgrade to 2.11
## `Connection::getParams()` has been marked internal
Consumers of the Connection class should not rely on connection parameters stored in the connection object. If needed, they should be obtained from a different source, e.g. application configuration.
## Deprecated `Doctrine\DBAL\Driver::getDatabase()`
- The usage of `Doctrine\DBAL\Driver::getDatabase()` is deprecated. Please use `Doctrine\DBAL\Connection::getDatabase()` instead.
- The behavior of the SQLite connection returning the database file path as the database is deprecated and shouldn't be relied upon.
## Deprecated `Portability\Connection::PORTABILITY_{PLATFORM}` constants`
The platform-specific portability mode flags are meant to be used only by the portability layer internally to optimize
the user-provided mode for the current database platform.
## Deprecated `MasterSlaveConnection` use `PrimaryReadReplicaConnection`
The `Doctrine\DBAL\Connections\MasterSlaveConnection` class is renamed to `Doctrine\DBAL\Connections\PrimaryReadReplicaConnection`.
In addition its configuration parameters `master`, `slaves` and `keepSlave` are renamed to `primary`, `replica` and `keepReplica`.
Before:
$connection = DriverManager::getConnection(
'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection',
'driver' => 'pdo_mysql',
'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
'slaves' => array(
array('user' => 'replica1', 'password', 'host' => '', 'dbname' => ''),
array('user' => 'replica2', 'password', 'host' => '', 'dbname' => ''),
),
'keepSlave' => true,
));
$connection->connect('slave');
$connection->connect('master');
$connection->isConnectedToMaster();
After:
$connection = DriverManager::getConnection(array(
'wrapperClass' => 'Doctrine\DBAL\Connections\PrimaryReadReplicaConnection',
'driver' => 'pdo_mysql',
'primary' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
'replica' => array(
array('user' => 'replica1', 'password', 'host' => '', 'dbname' => ''),
array('user' => 'replica2', 'password', 'host' => '', 'dbname' => ''),
)
'keepReplica' => true,
));
$connection->ensureConnectedToReplica();
$connection->ensureConnectedToPrimary();
$connection->isConnectedToPrimary();
## Deprecated `ArrayStatement` and `ResultCacheStatement` classes.
The `ArrayStatement` and `ResultCacheStatement` classes are deprecated. In a future major release they will be renamed and marked internal as implementation details of the caching layer.
......
......@@ -2,9 +2,7 @@
declare(strict_types=1);
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
use Doctrine\DBAL\Tools\Console\ConsoleRunner;
use Symfony\Component\Console\Helper\HelperSet;
$files = [__DIR__ . '/../vendor/autoload.php', __DIR__ . '/../../../autoload.php'];
$loader = null;
......@@ -45,16 +43,6 @@ if (! is_readable($configFile)) {
}
$commands = [];
$helperSetOrConnectionProvider = require $configFile;
$connectionProvider = require $configFile;
if (! $helperSetOrConnectionProvider instanceof HelperSet && ! $helperSetOrConnectionProvider instanceof ConnectionProvider) {
foreach ($GLOBALS as $candidate) {
if ($candidate instanceof HelperSet) {
$helperSetOrConnectionProvider = $candidate;
break;
}
}
}
ConsoleRunner::run($helperSetOrConnectionProvider, $commands);
ConsoleRunner::run($connectionProvider, $commands);
......@@ -11,18 +11,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="sqlsrv"/>
<var name="db_driver" value="sqlsrv"/>
<var name="db_host" value="(local)\SQL2008R2SP2" />
<var name="db_username" value="sa" />
<var name="db_user" value="sa" />
<var name="db_password" value="Password12!" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="1433" />
<var name="tmpdb_type" value="sqlsrv"/>
<var name="tmpdb_host" value="(local)\SQL2008R2SP2" />
<var name="tmpdb_username" value="sa" />
<var name="tmpdb_password" value="Password12!" />
<var name="tmpdb_port" value="1433" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -11,18 +11,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="sqlsrv"/>
<var name="db_driver" value="sqlsrv"/>
<var name="db_host" value="(local)\SQL2012SP1" />
<var name="db_username" value="sa" />
<var name="db_user" value="sa" />
<var name="db_password" value="Password12!" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="1433" />
<var name="tmpdb_type" value="sqlsrv"/>
<var name="tmpdb_host" value="(local)\SQL2012SP1" />
<var name="tmpdb_username" value="sa" />
<var name="tmpdb_password" value="Password12!" />
<var name="tmpdb_port" value="1433" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -11,18 +11,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_sqlsrv"/>
<var name="db_driver" value="pdo_sqlsrv"/>
<var name="db_host" value="(local)\SQL2017" />
<var name="db_username" value="sa" />
<var name="db_user" value="sa" />
<var name="db_password" value="Password12!" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="1433"/>
<var name="tmpdb_type" value="pdo_sqlsrv"/>
<var name="tmpdb_host" value="(local)\SQL2017" />
<var name="tmpdb_username" value="sa" />
<var name="tmpdb_password" value="Password12!" />
<var name="tmpdb_port" value="1433"/>
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -11,18 +11,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="sqlsrv"/>
<var name="db_driver" value="sqlsrv"/>
<var name="db_host" value="(local)\SQL2017" />
<var name="db_username" value="sa" />
<var name="db_user" value="sa" />
<var name="db_password" value="Password12!" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="1433" />
<var name="tmpdb_type" value="sqlsrv"/>
<var name="tmpdb_host" value="(local)\SQL2017" />
<var name="tmpdb_username" value="sa" />
<var name="tmpdb_password" value="Password12!" />
<var name="tmpdb_port" value="1433" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -11,20 +11,18 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="oci8"/>
<var name="db_driver" value="oci8"/>
<var name="db_host" value="oracle-xe-11" />
<var name="db_username" value="C##doctrine" />
<var name="db_user" value="C##doctrine" />
<var name="db_password" value="ORACLE" />
<var name="db_name" value="XE" />
<var name="db_port" value="1521"/>
<var name="db_dbname" value="XE" />
<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit"/>
<var name="tmpdb_type" value="oci8"/>
<var name="tmpdb_driver" value="oci8"/>
<var name="tmpdb_host" value="oracle-xe-11" />
<var name="tmpdb_username" value="ORACLE" />
<var name="tmpdb_user" value="ORACLE" />
<var name="tmpdb_password" value="ORACLE" />
<var name="tmpdb_name" value="XE" />
<var name="tmpdb_port" value="1521"/>
<var name="tmpdb_dbname" value="XE" />
</php>
<testsuites>
......
......@@ -9,20 +9,18 @@
bootstrap="bootstrap.php"
>
<php>
<var name="db_type" value="pdo_oci"/>
<var name="db_driver" value="pdo_oci"/>
<var name="db_host" value="oracle-xe-11" />
<var name="db_username" value="C##doctrine" />
<var name="db_user" value="C##doctrine" />
<var name="db_password" value="ORACLE" />
<var name="db_name" value="XE" />
<var name="db_port" value="1521"/>
<var name="db_dbname" value="XE" />
<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit"/>
<var name="tmpdb_type" value="pdo_oci"/>
<var name="tmpdb_driver" value="pdo_oci"/>
<var name="tmpdb_host" value="oracle-xe-11" />
<var name="tmpdb_username" value="ORACLE" />
<var name="tmpdb_user" value="ORACLE" />
<var name="tmpdb_password" value="ORACLE" />
<var name="tmpdb_name" value="XE" />
<var name="tmpdb_port" value="1521"/>
<var name="tmpdb_dbname" value="XE" />
</php>
<testsuites>
......
......@@ -10,19 +10,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="ibm_db2"/>
<var name="db_driver" value="ibm_db2"/>
<var name="db_host" value="127.0.0.1"/>
<var name="db_username" value="db2inst1"/>
<var name="db_user" value="db2inst1"/>
<var name="db_password" value="Doctrine2018"/>
<var name="db_name" value="HOSTNAME=127.0.0.1;UID=db2inst1;PWD=Doctrine2018;DATABASE=doctrine"/>
<var name="db_port" value="50000"/>
<var name="tmpdb_type" value="ibm_db2"/>
<var name="tmpdb_host" value="127.0.0.1"/>
<var name="tmpdb_username" value="db2inst1"/>
<var name="tmpdb_password" value="Doctrine2018"/>
<var name="tmpdb_name" value="HOSTNAME=127.0.0.1;UID=db2inst1;PWD=Doctrine2018;DATABASE=doctrine"/>
<var name="tmpdb_port" value="50000"/>
<var name="db_dbname" value="doctrine"/>
</php>
<testsuites>
......
......@@ -10,18 +10,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_mysql"/>
<var name="db_driver" value="pdo_mysql"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_username" value="root" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="33306"/>
<var name="tmpdb_type" value="pdo_mysql"/>
<var name="tmpdb_host" value="127.0.0.1" />
<var name="tmpdb_username" value="root" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="33306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -10,18 +10,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="mysqli"/>
<var name="db_driver" value="mysqli"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_username" value="root" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="33306"/>
<var name="tmpdb_type" value="mysqli"/>
<var name="tmpdb_host" value="127.0.0.1" />
<var name="tmpdb_username" value="root" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="33306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -10,18 +10,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_mysql"/>
<var name="db_driver" value="pdo_mysql"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_username" value="root" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="33306"/>
<var name="tmpdb_type" value="pdo_mysql"/>
<var name="tmpdb_host" value="127.0.0.1" />
<var name="tmpdb_username" value="root" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="33306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -10,18 +10,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="mysqli"/>
<var name="db_driver" value="mysqli"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_username" value="root" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="33306"/>
<var name="tmpdb_type" value="mysqli"/>
<var name="tmpdb_host" value="127.0.0.1" />
<var name="tmpdb_username" value="root" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="33306"/>
<var name="db_user" value="root" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -10,18 +10,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_sqlsrv"/>
<var name="db_driver" value="pdo_sqlsrv"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_username" value="sa" />
<var name="db_user" value="sa" />
<var name="db_password" value="Doctrine2018" />
<var name="db_name" value="doctrine" />
<var name="db_port" value="1433"/>
<var name="tmpdb_type" value="pdo_sqlsrv"/>
<var name="tmpdb_host" value="127.0.0.1" />
<var name="tmpdb_username" value="sa" />
<var name="tmpdb_password" value="Doctrine2018" />
<var name="tmpdb_port" value="1433"/>
<var name="db_dbname" value="doctrine" />
</php>
<testsuites>
......
......@@ -10,18 +10,10 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="pdo_pgsql"/>
<var name="db_driver" value="pdo_pgsql"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="postgres" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="5432"/>
<var name="tmpdb_type" value="pdo_pgsql"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="postgres" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="5432"/>
<var name="db_user" value="postgres" />
<var name="db_dbname" value="doctrine_tests" />
</php>
<testsuites>
......
......@@ -10,18 +10,11 @@
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="sqlsrv"/>
<var name="db_driver" value="sqlsrv"/>
<var name="db_host" value="127.0.0.1" />
<var name="db_username" value="sa" />
<var name="db_user" value="sa" />
<var name="db_password" value="Doctrine2018" />
<var name="db_name" value="doctrine" />
<var name="db_port" value="1433"/>
<var name="tmpdb_type" value="sqlsrv"/>
<var name="tmpdb_host" value="127.0.0.1" />
<var name="tmpdb_username" value="sa" />
<var name="tmpdb_password" value="Doctrine2018" />
<var name="tmpdb_port" value="1433"/>
<var name="db_dbname" value="doctrine" />
</php>
<testsuites>
......
......@@ -18,7 +18,6 @@
"queryobject",
"sasql",
"sql",
"sqlanywhere",
"sqlite",
"sqlserver",
"sqlsrv"
......@@ -33,9 +32,9 @@
],
"require": {
"php": "^7.3 || ^8.0",
"composer/package-versions-deprecated": "^1.8",
"doctrine/cache": "^1.0",
"doctrine/event-manager": "^1.0",
"ocramius/package-versions": "^1.4"
"doctrine/event-manager": "^1.0"
},
"require-dev": {
"doctrine/coding-standard": "^8.0",
......@@ -44,7 +43,7 @@
"phpstan/phpstan": "^0.12.18",
"phpstan/phpstan-phpunit": "^0.12",
"phpstan/phpstan-strict-rules": "^0.12.2",
"phpunit/phpunit": "^9.1.1",
"phpunit/phpunit": "^9.2",
"psalm/plugin-phpunit": "^0.10.0",
"symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
"vimeo/psalm": "^3.11.4"
......
This diff is collapsed.
......@@ -133,7 +133,6 @@ interfaces to use. It can be configured in one of three ways:
- ``pdo_sqlsrv``: A Microsoft SQL Server driver that uses pdo_sqlsrv PDO
- ``sqlsrv``: A Microsoft SQL Server driver that uses the sqlsrv PHP extension.
- ``oci8``: An Oracle driver that uses the oci8 PHP extension.
- ``sqlanywhere``: A SAP Sybase SQL Anywhere driver that uses the sqlanywhere PHP extension.
- ``driverClass``: Specifies a custom driver implementation if no
'driver' is specified. This allows the use of custom drivers that
......@@ -292,27 +291,6 @@ pdo_sqlsrv / sqlsrv
- ``port`` (integer): Port of the database to connect to.
- ``dbname`` (string): Name of the database/schema to connect to.
sqlanywhere
^^^^^^^^^^^
- ``user`` (string): Username to use when connecting to the
database.
- ``password`` (string): Password to use when connecting to the
database.
- ``server`` (string): Name of a running database server to connect to.
- ``host`` (string): Hostname of the database to connect to.
- ``port`` (integer): Port of the database to connect to.
- ``dbname`` (string): Name of the database/schema to connect to.
- ``persistent`` (boolean): Whether to establish a persistent connection.
Depending on the used underlying platform version, you can specify
any other connection parameter that is supported by the particular
platform version via the ``driverOptions`` option.
You can find a list of supported connection parameters for the
currently supported platform here:
- `SAP Sybase SQL Anywhere 16.0 <http://dcx.sybase.com/index.html#sa160/en/dbadmin/da-conparm.html>`_
Automatic platform version detection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -337,11 +315,7 @@ without any extra configuration required:
- ``sqlsrv``
Some drivers cannot provide the version of the underlying database server without
having to query for it explicitly. For performance reasons (to save one extra query
on every connect), Doctrine does not enable automatic database platform version
detection for the following drivers:
- ``sqlanywhere``
having to query for it explicitly.
If you still want to tell Doctrine which database server version you are using in
order to choose the appropriate platform implementation, you can pass the
......
......@@ -19,7 +19,6 @@ The following database vendors are currently supported:
- Oracle
- Microsoft SQL Server
- PostgreSQL
- SAP Sybase SQL Anywhere
- SQLite
The Doctrine 2 database layer can be used independently of the
......
......@@ -58,11 +58,6 @@ PostgreSQL
- ``PostgreSQL94Platform`` for version 9.4 and above.
- ``PostgreSQL100Platform`` for version 10.0 and above.
SAP Sybase SQL Anywhere
^^^^^^^^^^^^^^^^^^^^^^^
- ``SQLAnywhere16Platform`` for version 16 and above.
SQLite
^^^^^^
......
......@@ -4,7 +4,7 @@ Portability
There are often cases when you need to write an application or library that is portable
across multiple different database vendors. The Doctrine ORM is one example of such
a library. It is an abstraction layer over all the currently supported vendors (MySQL, Oracle,
PostgreSQL, SQLite, SAP SQL Anywhere and Microsoft SQL Server). If you want to use the DBAL
PostgreSQL, SQLite and Microsoft SQL Server). If you want to use the DBAL
to write a portable application or library you have to follow lots of rules to make
all the different vendors work the same.
......
......@@ -116,10 +116,10 @@ The following options are not completely portable but are supported by most of t
vendors:
- **unsigned** (boolean): Whether a ``smallint``, ``integer`` or ``bigint`` Doctrine
type column should allow unsigned values only. Supported by MySQL and SQL Anywhere.
type column should allow unsigned values only. Supported only by MySQL.
Defaults to ``false``.
- **comment** (integer|string): The column comment. Supported by MySQL, PostgreSQL,
Oracle, SQL Server and SQL Anywhere. Defaults to ``null``.
Oracle and SQL Server. Defaults to ``null``.
Vendor specific options
^^^^^^^^^^^^^^^^^^^^^^^
......
This diff is collapsed.
......@@ -85,11 +85,12 @@
<!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps">
<!--
This file uses the return value db2_server_info(), which does not follow conventions
phpcs wrongly complains about it, and that has been reported here:
These files use the underlying driver APIs that don't comply with the coding standard
phpcs wrongly complains about them, and that has been reported here:
https://github.com/squizlabs/PHP_CodeSniffer/issues/2950
-->
<exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern>
<exclude-pattern>src/Driver/Mysqli/MysqliConnection.php</exclude-pattern>
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<exclude-pattern>src/SQLParserUtils.php</exclude-pattern>
<exclude-pattern>src/Tools/Dumper.php</exclude-pattern>
......@@ -103,6 +104,11 @@
<exclude-pattern>tests/Functional/ResultCacheTest.php</exclude-pattern>
</rule>
<!-- See https://github.com/slevomat/coding-standard/issues/1038 -->
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
<exclude-pattern>tests/Functional/Driver/IBMDB2/DB2StatementTest.php</exclude-pattern>
</rule>
<!-- see https://github.com/doctrine/dbal/issues/3377 -->
<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator">
<exclude-pattern>src/Schema/Comparator.php</exclude-pattern>
......
......@@ -49,6 +49,16 @@ parameters:
message: '~^Cannot cast array<string>\|bool\|string\|null to int\.$~'
path: %currentWorkingDirectory%/src/Tools/Console/Command/RunSqlCommand.php
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/553
-
message: '~^Call to function assert\(\) with true will always evaluate to true\.$~'
path: %currentWorkingDirectory%/src/Driver/PDOConnection.php
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/553
-
message: '~^Strict comparison using !== between int and false will always evaluate to true\.$~'
path: %currentWorkingDirectory%/src/Driver/PDOConnection.php
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/732
-
message: '~^Access to undefined constant PDO::PGSQL_ATTR_DISABLE_PREPARES\.$~'
......@@ -112,6 +122,16 @@ parameters:
message: '~^Call to an undefined method Doctrine\\DBAL\\Driver::createDatabasePlatformForVersion\(\)\.$~'
path: %currentWorkingDirectory%/tests/Driver/AbstractDriverTest.php
# This is not reproducible on PHPStan 0.12.30
-
message: '~^Method Doctrine\\DBAL\\Tests\\Functional\\Driver\\Mysqli\\ConnectionTest::getConnection\(\) should return Doctrine\\DBAL\\Driver\\Mysqli\\MysqliConnection but returns Doctrine\\DBAL\\Driver\\Connection\.$~'
path: %currentWorkingDirectory%/tests/Functional/Driver/Mysqli/ConnectionTest.php
# This is not reproducible on PHPStan 0.12.30
-
message: '~^Method Doctrine\\DBAL\\Tests\\Functional\\Driver\\PDOSqlsrv\\DriverTest::getConnection\(\) should return Doctrine\\DBAL\\Driver\\PDOConnection but returns Doctrine\\DBAL\\Driver\\Connection\.$~'
path: %currentWorkingDirectory%/tests/Functional/Driver/PDOSqlsrv/DriverTest.php
# Caused by phpdoc annotations intended for Psalm
-
message: '~Unable to resolve the template type T in call to method static method Doctrine\\DBAL\\DriverManager::getConnection\(\)~'
......@@ -125,8 +145,8 @@ parameters:
- %currentWorkingDirectory%/tests/Functional/Driver/SQLAnywhere/ConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Driver/SQLAnywhere/StatementTest.php
- %currentWorkingDirectory%/tests/Functional/ExceptionTest.php
- %currentWorkingDirectory%/tests/Functional/MasterSlaveConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/PortabilityTest.php
- %currentWorkingDirectory%/tests/Functional/PrimaryReadReplicaConnectionTest.php
- %currentWorkingDirectory%/tests/Functional/Schema/MySqlSchemaManagerTest.php
- %currentWorkingDirectory%/tests/Schema/Synchronizer/SingleDatabaseSynchronizerTest.php
- %currentWorkingDirectory%/tests/TestUtil.php
......@@ -136,6 +156,10 @@ parameters:
paths:
- %currentWorkingDirectory%/src/Driver/PDOSqlsrv/Connection.php
-
message: '~Return type \(Doctrine\\DBAL\\Portability\\Statement\) of method Doctrine\\DBAL\\Portability\\Connection::prepare\(\) should be compatible with return type \(Doctrine\\DBAL\\Statement\) of method Doctrine\\DBAL\\Connection::prepare\(\)~'
paths:
- %currentWorkingDirectory%/src/Portability/Connection.php
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
......
......@@ -22,24 +22,24 @@
<php>
<ini name="error_reporting" value="-1" />
<!-- "Real" test database -->
<!-- Test connection parameters -->
<!-- Uncomment, otherwise SQLite runs
<var name="db_type" value="pdo_mysql"/>
<var name="db_driver" value="pdo_mysql"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="root" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>
<var name="db_user" value="root" />
<var name="db_password" value="" />
<var name="db_dbname" value="doctrine_tests" />
-->
<!--<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit">-->
<!-- Database for temporary connections (i.e. to drop/create the main database) -->
<var name="tmpdb_type" value="pdo_mysql"/>
<!-- Privileged user connection parameters. Used to create and drop the test database -->
<var name="tmpdb_driver" value="pdo_mysql"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="root" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_name" value="doctrine_tests_tmp" />
<var name="tmpdb_port" value="3306"/>
<var name="tmpdb_user" value="root" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_dbname" value="doctrine_tests_tmp" />
</php>
<testsuites>
......
<?xml version="1.0"?>
<psalm
totallyTyped="false"
errorLevel="6"
errorLevel="5"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
......@@ -32,6 +32,15 @@
<file name="src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php"/>
</errorLevel>
</ConflictingReferenceConstraint>
<FalsableReturnStatement>
<errorLevel type="suppress">
<!--
Fixing these issues requires an API change
-->
<file name="src/Driver/PDOSqlsrv/Connection.php"/>
<file name="src/Driver/SQLSrv/SQLSrvConnection.php"/>
</errorLevel>
</FalsableReturnStatement>
<ImpureMethodCall>
<errorLevel type="suppress">
<!--
......@@ -41,6 +50,14 @@
<file name="src/Exception/DriverException.php"/>
</errorLevel>
</ImpureMethodCall>
<NullableReturnStatement>
<errorLevel type="suppress">
<!--
Fixing this issue requires an API change
-->
<file name="src/Driver/AbstractSQLiteDriver.php"/>
</errorLevel>
</NullableReturnStatement>
<TooFewArguments>
<errorLevel type="suppress">
<!--
......@@ -52,7 +69,6 @@
</TooFewArguments>
<UndefinedConstant>
<errorLevel type="suppress">
<directory name="src/Driver/SQLAnywhere"/>
<!--
Requires a release of
https://github.com/JetBrains/phpstorm-stubs/pull/732
......@@ -60,11 +76,6 @@
<file name="tests/Driver/PDOPgSql/DriverTest.php" />
</errorLevel>
</UndefinedConstant>
<UndefinedFunction>
<errorLevel type="suppress">
<directory name="src/Driver/SQLAnywhere"/>
</errorLevel>
</UndefinedFunction>
<UndefinedClass>
<errorLevel type="suppress">
<!-- Contains references to optional dependencies -->
......
......@@ -89,11 +89,7 @@ final class CachingResult implements Result
*/
public function fetchAllNumeric(): array
{
$this->store(
$this->result->fetchAllAssociative()
);
return array_map('array_values', $this->data);
return array_map('array_values', $this->result->fetchAllAssociative());
}
/**
......@@ -101,11 +97,11 @@ final class CachingResult implements Result
*/
public function fetchAllAssociative(): array
{
$this->store(
$this->result->fetchAllAssociative()
);
$data = $this->result->fetchAllAssociative();
$this->store($data);
return $this->data;
return $data;
}
/**
......
......@@ -80,13 +80,6 @@ class Connection implements DriverConnection
/** @var ExpressionBuilder */
protected $_expr;
/**
* Whether or not a connection has been established.
*
* @var bool
*/
private $isConnected = false;
/**
* The current auto-commit mode of this connection.
*
......@@ -198,6 +191,8 @@ class Connection implements DriverConnection
/**
* Gets the parameters used during instantiation.
*
* @internal
*
* @return array<string, mixed>
*/
public function getParams(): array
......@@ -278,16 +273,15 @@ class Connection implements DriverConnection
*/
public function connect(): void
{
if ($this->isConnected) {
if ($this->_conn !== null) {
return;
}
$driverOptions = $this->params['driverOptions'] ?? [];
$user = $this->params['user'] ?? '';
$password = $this->params['password'] ?? '';
$this->_conn = $this->_driver->connect($this->params, $user, $password, $driverOptions);
$this->isConnected = true;
try {
$this->_conn = $this->_driver->connect($this->params);
} catch (DriverException $e) {
throw DBALException::driverException($this->_driver, $e);
}
$this->transactionNestingLevel = 0;
......@@ -351,7 +345,7 @@ class Connection implements DriverConnection
if ($this->_conn === null) {
try {
$this->connect();
} catch (Throwable $originalException) {
} catch (DBALException $originalException) {
if (! isset($this->params['dbname'])) {
throw $originalException;
}
......@@ -363,7 +357,7 @@ class Connection implements DriverConnection
try {
$this->connect();
} catch (Throwable $fallbackException) {
} catch (DBALException $fallbackException) {
// Either the platform does not support database-less connections
// or something else went wrong.
// Reset connection parameters and rethrow the original exception.
......@@ -439,7 +433,7 @@ class Connection implements DriverConnection
$this->autoCommit = $autoCommit;
// Commit all currently active transactions if any when switching auto-commit mode.
if (! $this->isConnected || $this->transactionNestingLevel === 0) {
if ($this->_conn === null || $this->transactionNestingLevel === 0) {
return;
}
......@@ -514,7 +508,7 @@ class Connection implements DriverConnection
*/
public function isConnected(): bool
{
return $this->isConnected;
return $this->_conn !== null;
}
/**
......@@ -594,8 +588,6 @@ class Connection implements DriverConnection
public function close(): void
{
$this->_conn = null;
$this->isConnected = false;
}
/**
......@@ -877,6 +869,8 @@ class Connection implements DriverConnection
*
* @param string $sql The SQL statement to prepare.
*
* @return Statement
*
* @throws DBALException
*/
public function prepare(string $sql): DriverStatement
......@@ -958,7 +952,7 @@ class Connection implements DriverConnection
throw NoResultDriverConfigured::new();
}
$connectionParams = $this->getParams();
$connectionParams = $this->params;
unset($connectionParams['platform']);
[$cacheKey, $realKey] = $qcp->generateCacheKeys($query, $params, $types, $connectionParams);
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
......@@ -17,19 +18,13 @@ interface Driver
/**
* Attempts to create a connection with the database.
*
* @param mixed[] $params All connection parameters passed by the user.
* @param string $username The username to use when connecting.
* @param string $password The password to use when connecting.
* @param mixed[] $driverOptions The driver options to use when connecting.
* @param mixed[] $params All connection parameters.
*
* @return DriverConnection The database connection.
*
* @throws DriverException
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): DriverConnection;
public function connect(array $params): DriverConnection;
/**
* Gets the DatabasePlatform instance that provides all the metadata about
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\Exception\InvalidPlatformVersion;
use Doctrine\DBAL\Platforms\SQLAnywhere16Platform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\SQLAnywhereSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use function preg_match;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SAP Sybase SQL Anywhere based drivers.
*/
abstract class AbstractSQLAnywhereDriver implements ExceptionConverterDriver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @link http://dcx.sybase.com/index.html#sa160/en/saerrors/sqlerror.html
*/
public function convertException(string $message, DriverExceptionInterface $exception): DriverException
{
switch ($exception->getCode()) {
case -306:
case -307:
case -684:
return new Exception\DeadlockException($message, $exception);
case -210:
case -1175:
case -1281:
return new Exception\LockWaitTimeoutException($message, $exception);
case -100:
case -103:
case -832:
return new Exception\ConnectionException($message, $exception);
case -143:
return new Exception\InvalidFieldNameException($message, $exception);
case -193:
case -196:
return new Exception\UniqueConstraintViolationException($message, $exception);
case -194:
case -198:
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
case -144:
return new Exception\NonUniqueFieldNameException($message, $exception);
case -184:
case -195:
return new Exception\NotNullConstraintViolationException($message, $exception);
case -131:
return new Exception\SyntaxErrorException($message, $exception);
case -110:
return new Exception\TableExistsException($message, $exception);
case -141:
case -1041:
return new Exception\TableNotFoundException($message, $exception);
}
return new DriverException($message, $exception);
}
public function createDatabasePlatformForVersion(string $version): AbstractPlatform
{
if (
preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+)(?:\.(?P<build>\d+))?)?)?/',
$version,
$versionParts
) === 0
) {
throw InvalidPlatformVersion::new(
$version,
'<major_version>.<minor_version>.<patch_version>.<build_version>'
);
}
return new SQLAnywhere16Platform();
}
public function getDatabasePlatform(): AbstractPlatform
{
return new SQLAnywhere16Platform();
}
public function getSchemaManager(Connection $conn): AbstractSchemaManager
{
return new SQLAnywhereSchemaManager($conn);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\AbstractSQLServerDriver;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* @internal
*
* @psalm-immutable
*/
final class PortWithoutHost extends AbstractDriverException
{
public static function new(): self
{
return new self('Connection port specified without the host');
}
}
......@@ -4,6 +4,9 @@ declare(strict_types=0);
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
......@@ -21,6 +24,7 @@ use function db2_pconnect;
use function db2_prepare;
use function db2_rollback;
use function db2_server_info;
use function error_get_last;
use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON;
......@@ -31,21 +35,25 @@ final class DB2Connection implements ServerInfoAwareConnection
private $conn;
/**
* @param array<string, mixed> $params
* @param array<string, mixed> $driverOptions
* @param array<string,mixed> $driverOptions
*
* @throws DB2Exception
*/
public function __construct(array $params, string $username, string $password, array $driverOptions = [])
{
if (isset($params['persistent']) && $params['persistent'] === true) {
$conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions);
public function __construct(
string $database,
bool $persistent,
string $username,
string $password,
array $driverOptions = []
) {
if ($persistent) {
$conn = db2_pconnect($database, $username, $password, $driverOptions);
} else {
$conn = db2_connect($params['dbname'], $username, $password, $driverOptions);
$conn = db2_connect($database, $username, $password, $driverOptions);
}
if ($conn === false) {
throw DB2Exception::fromConnectionError();
throw ConnectionFailed::new();
}
$this->conn = $conn;
......@@ -62,8 +70,9 @@ final class DB2Connection implements ServerInfoAwareConnection
public function prepare(string $sql): DriverStatement
{
$stmt = @db2_prepare($this->conn, $sql);
if ($stmt === false) {
throw DB2Exception::fromStatementError();
throw PrepareFailed::new(error_get_last()['message']);
}
return new DB2Statement($stmt);
......@@ -84,7 +93,7 @@ final class DB2Connection implements ServerInfoAwareConnection
$stmt = @db2_exec($this->conn, $statement);
if ($stmt === false) {
throw DB2Exception::fromStatementError();
throw ConnectionError::new($this->conn);
}
return db2_num_rows($stmt);
......@@ -98,29 +107,29 @@ final class DB2Connection implements ServerInfoAwareConnection
public function beginTransaction(): void
{
if (db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF) !== true) {
throw DB2Exception::fromConnectionError($this->conn);
throw ConnectionError::new($this->conn);
}
}
public function commit(): void
{
if (! db2_commit($this->conn)) {
throw DB2Exception::fromConnectionError($this->conn);
throw ConnectionError::new($this->conn);
}
if (db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON) !== true) {
throw DB2Exception::fromConnectionError($this->conn);
throw ConnectionError::new($this->conn);
}
}
public function rollBack(): void
{
if (! db2_rollback($this->conn)) {
throw DB2Exception::fromConnectionError($this->conn);
throw ConnectionError::new($this->conn);
}
if (db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON) !== true) {
throw DB2Exception::fromConnectionError($this->conn);
throw ConnectionError::new($this->conn);
}
}
}
......@@ -7,11 +7,6 @@ namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\AbstractDB2Driver;
use Doctrine\DBAL\Driver\Connection;
use function array_keys;
use function array_map;
use function implode;
use function sprintf;
/**
* IBM DB2 Driver.
*/
......@@ -20,42 +15,14 @@ final class DB2Driver extends AbstractDB2Driver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
if ($params['host'] !== 'localhost' && $params['host'] !== '127.0.0.1') {
// if the host isn't localhost, use extended connection params
$params['dbname'] = $this->buildConnectionString($params, $username, $password);
$username = $password = '';
}
return new DB2Connection($params, $username, $password, $driverOptions);
}
/**
* @param string[] $params
*/
private function buildConnectionString(array $params, string $username, string $password): string
public function connect(array $params): Connection
{
$connectionParams = [
'DRIVER' => '{IBM DB2 ODBC DRIVER}',
'DATABASE' => $params['dbname'],
'HOSTNAME' => $params['host'],
'PROTOCOL' => $params['protocol'] ?? 'TCPIP',
'UID' => $username,
'PWD' => $password,
];
if (isset($params['port'])) {
$connectionParams['PORT'] = $params['port'];
}
return implode(';', array_map(static function (string $key, string $value): string {
return sprintf('%s=%s', $key, $value);
}, array_keys($connectionParams), $connectionParams));
return new DB2Connection(
DataSourceName::fromConnectionParameters($params)->toString(),
isset($params['persistent']) && $params['persistent'] === true,
$params['user'] ?? '',
$params['password'] ?? '',
$params['driver_options'] ?? []
);
}
}
......@@ -6,37 +6,9 @@ namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\AbstractDriverException;
use function db2_conn_error;
use function db2_conn_errormsg;
use function db2_stmt_error;
use function db2_stmt_errormsg;
/**
* @psalm-immutable
*/
class DB2Exception extends AbstractDriverException
{
/**
* @param resource|null $connection
*/
public static function fromConnectionError($connection = null): self
{
if ($connection !== null) {
return new self(db2_conn_errormsg($connection), db2_conn_error($connection));
}
return new self(db2_conn_errormsg(), db2_conn_error());
}
/**
* @param resource|null $statement
*/
public static function fromStatementError($statement = null): self
{
if ($statement !== null) {
return new self(db2_stmt_errormsg($statement), db2_stmt_error($statement));
}
return new self(db2_stmt_errormsg(), db2_stmt_error());
}
}
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\ParameterType;
......@@ -56,6 +57,8 @@ final class DB2Statement implements Statement
*/
public function bindValue($param, $value, int $type = ParameterType::STRING): void
{
assert(is_int($param));
$this->bindParam($param, $value, $type);
}
......@@ -102,7 +105,7 @@ final class DB2Statement implements Statement
$this->bindParam[$position] =& $variable;
if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
throw DB2Exception::fromStatementError($this->stmt);
throw StatementError::new($this->stmt);
}
}
......@@ -140,7 +143,7 @@ final class DB2Statement implements Statement
$this->lobs = [];
if ($result === false) {
throw DB2Exception::fromStatementError($this->stmt);
throw StatementError::new($this->stmt);
}
return new Result($this->stmt);
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use function implode;
use function sprintf;
use function strpos;
/**
* IBM DB2 DSN
*/
final class DataSourceName
{
/** @var string */
private $string;
private function __construct(string $string)
{
$this->string = $string;
}
public function toString(): string
{
return $this->string;
}
/**
* Creates the object from an array representation
*
* @param array<string,mixed> $params
*/
public static function fromArray(array $params): self
{
$chunks = [];
foreach ($params as $key => $value) {
$chunks[] = sprintf('%s=%s', $key, $value);
}
return new self(implode(';', $chunks));
}
/**
* Creates the object from the given DBAL connection parameters.
*
* @param array<string,mixed> $params
*/
public static function fromConnectionParameters(array $params): self
{
if (isset($params['dbname']) && strpos($params['dbname'], '=') !== false) {
return new self($params['dbname']);
}
$dsnParams = [];
foreach (
[
'host' => 'HOSTNAME',
'port' => 'PORT',
'protocol' => 'PROTOCOL',
'dbname' => 'DATABASE',
'user' => 'UID',
'password' => 'PWD',
] as $dbalParam => $dsnParam
) {
if (! isset($params[$dbalParam])) {
continue;
}
$dsnParams[$dsnParam] = $params[$dbalParam];
}
return self::fromArray($dsnParams);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @psalm-immutable
*/
final class ConnectionError extends DB2Exception
{
/**
* @param resource $connection
*/
public static function new($connection): self
{
return new self(db2_conn_errormsg($connection), db2_conn_error($connection));
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @psalm-immutable
*/
final class ConnectionFailed extends DB2Exception
{
public static function new(): self
{
return new self(db2_conn_errormsg(), db2_conn_error());
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
/**
* @psalm-immutable
*/
final class PrepareFailed extends DB2Exception
{
public static function new(string $message): self
{
return new self($message);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use function db2_stmt_error;
use function db2_stmt_errormsg;
/**
* @psalm-immutable
*/
final class StatementError extends DB2Exception
{
/**
* @param resource $statement
*/
public static function new($statement): self
{
return new self(db2_stmt_errormsg($statement), db2_stmt_error($statement));
}
}
......@@ -4,25 +4,118 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Charset;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Options;
use Doctrine\DBAL\Driver\Mysqli\Initializer\Secure;
use function count;
final class Driver extends AbstractMySQLDriver
{
/**
* {@inheritdoc}
*
* @return MysqliConnection
*/
public function connect(array $params): Connection
{
if (! empty($params['persistent'])) {
if (! isset($params['host'])) {
throw HostRequired::forPersistentConnection();
}
$host = 'p:' . $params['host'];
} else {
$host = $params['host'] ?? '';
}
$flags = 0;
$preInitializers = $postInitializers = [];
if (isset($params['driver_options'])) {
$driverOptions = $params['driver_options'];
if (isset($driverOptions[MysqliConnection::OPTION_FLAGS])) {
$flags = $driverOptions[MysqliConnection::OPTION_FLAGS];
unset($driverOptions[MysqliConnection::OPTION_FLAGS]);
}
$preInitializers = $this->withOptions($preInitializers, $driverOptions);
}
$preInitializers = $this->withSecure($preInitializers, $params);
$postInitializers = $this->withCharset($postInitializers, $params);
return new MysqliConnection(
$host,
$params['user'] ?? '',
$params['password'] ?? '',
$params['dbname'] ?? '',
$params['port'] ?? 0,
$params['unix_socket'] ?? '',
$flags,
$preInitializers,
$postInitializers
);
}
/**
* @param list<Initializer> $initializers
* @param array<int,mixed> $options
*
* @return list<Initializer>
*/
private function withOptions(array $initializers, array $options): array
{
if (count($options) !== 0) {
$initializers[] = new Options($options);
}
return $initializers;
}
/**
* @param list<Initializer> $initializers
* @param array<string,mixed> $params
*
* @return list<Initializer>
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
try {
return new MysqliConnection($params, $username, $password, $driverOptions);
} catch (MysqliException $e) {
throw DBALException::driverException($this, $e);
private function withSecure(array $initializers, array $params): array
{
if (
isset($params['ssl_key']) ||
isset($params['ssl_cert']) ||
isset($params['ssl_ca']) ||
isset($params['ssl_capath']) ||
isset($params['ssl_cipher'])
) {
$initializers[] = new Secure(
$params['ssl_key'] ?? null,
$params['ssl_cert'] ?? null,
$params['ssl_ca'] ?? null,
$params['ssl_capath'] ?? null,
$params['ssl_cipher'] ?? null
);
}
return $initializers;
}
/**
* @param list<Initializer> $initializers
* @param array<string,mixed> $params
*
* @return list<Initializer>
*/
private function withCharset(array $initializers, array $params): array
{
if (isset($params['charset'])) {
$initializers[] = new Charset($params['charset']);
}
return $initializers;
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use mysqli;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class InvalidCharset extends MysqliException
{
public static function fromCharset(mysqli $connection, string $charset): self
{
return new self(
sprintf('Failed to set charset "%s": %s', $charset, $connection->error),
$connection->sqlstate,
$connection->errno
);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class InvalidOption extends MysqliException
{
/**
* @param mixed $value
*/
public static function fromOption(int $option, $value): self
{
return new self(
sprintf('Failed to set option %d with value "%s"', $option, $value)
);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli;
use mysqli;
interface Initializer
{
/**
* @throws MysqliException
*/
public function initialize(mysqli $connection): void;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidCharset;
use Doctrine\DBAL\Driver\Mysqli\Initializer;
use mysqli;
final class Charset implements Initializer
{
/** @var string */
private $charset;
public function __construct(string $charset)
{
$this->charset = $charset;
}
public function initialize(mysqli $connection): void
{
if ($connection->set_charset($this->charset)) {
return;
}
throw InvalidCharset::fromCharset($connection, $this->charset);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidOption;
use Doctrine\DBAL\Driver\Mysqli\Initializer;
use mysqli;
use function mysqli_options;
final class Options implements Initializer
{
/** @var array<int,mixed> */
private $options;
/**
* @param array<int,mixed> $options
*/
public function __construct(array $options)
{
$this->options = $options;
}
public function initialize(mysqli $connection): void
{
foreach ($this->options as $option => $value) {
if (! mysqli_options($connection, $option, $value)) {
throw InvalidOption::fromOption($option, $value);
}
}
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Initializer;
use Doctrine\DBAL\Driver\Mysqli\Initializer;
use mysqli;
final class Secure implements Initializer
{
/** @var string|null */
private $key;
/** @var string|null */
private $cert;
/** @var string|null */
private $ca;
/** @var string|null */
private $capath;
/** @var string|null */
private $cipher;
public function __construct(?string $key, ?string $cert, ?string $ca, ?string $capath, ?string $cipher)
{
$this->key = $key;
$this->cert = $cert;
$this->ca = $ca;
$this->capath = $capath;
$this->cipher = $cipher;
}
public function initialize(mysqli $connection): void
{
$connection->ssl_set($this->key, $this->cert, $this->ca, $this->capath, $this->cipher);
}
}
......@@ -11,24 +11,10 @@ use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use mysqli;
use function defined;
use function floor;
use function in_array;
use function ini_get;
use function mysqli_init;
use function mysqli_options;
use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use function stripos;
use const MYSQLI_INIT_COMMAND;
use const MYSQLI_OPT_CONNECT_TIMEOUT;
use const MYSQLI_OPT_LOCAL_INFILE;
use const MYSQLI_READ_DEFAULT_FILE;
use const MYSQLI_READ_DEFAULT_GROUP;
use const MYSQLI_SERVER_PUBLIC_KEY;
class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
{
/**
......@@ -40,51 +26,41 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
private $conn;
/**
* @param array<string, mixed> $params
* @param array<int, mixed> $driverOptions
* @param iterable<Initializer> $preInitializers
* @param iterable<Initializer> $postInitializers
*
* @throws MysqliException
*/
public function __construct(array $params, string $username, string $password, array $driverOptions = [])
{
$socket = $params['unix_socket'] ?? ini_get('mysqli.default_socket');
$dbname = $params['dbname'] ?? '';
$port = $params['port'] ?? 0;
if (! empty($params['persistent'])) {
if (! isset($params['host'])) {
throw HostRequired::forPersistentConnection();
}
public function __construct(
string $host = '',
string $username = '',
string $password = '',
string $database = '',
int $port = 0,
string $socket = '',
int $flags = 0,
iterable $preInitializers = [],
iterable $postInitializers = []
) {
$connection = mysqli_init();
$host = 'p:' . $params['host'];
} else {
$host = $params['host'] ?? null;
foreach ($preInitializers as $initializer) {
$initializer->initialize($connection);
}
$flags = $driverOptions[static::OPTION_FLAGS] ?? 0;
$this->conn = mysqli_init();
$this->setSecureConnection($params);
$this->setDriverOptions($driverOptions);
set_error_handler(static function (): bool {
return true;
});
try {
if (! $this->conn->real_connect($host, $username, $password, $dbname, $port, $socket, $flags)) {
throw ConnectionError::new($this->conn);
}
} finally {
restore_error_handler();
if (! @$connection->real_connect($host, $username, $password, $database, $port, $socket, $flags)) {
throw new MysqliException(
$connection->connect_error,
'HY000',
$connection->connect_errno
);
}
if (! isset($params['charset'])) {
return;
foreach ($postInitializers as $initializer) {
$initializer->initialize($connection);
}
$this->conn->set_charset($params['charset']);
$this->conn = $connection;
}
/**
......@@ -167,49 +143,6 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
}
}
/**
* Apply the driver options to the connection.
*
* @param array<int, mixed> $driverOptions
*
* @throws MysqliException When one of of the options is not supported.
* @throws MysqliException When applying doesn't work - e.g. due to incorrect value.
*/
private function setDriverOptions(array $driverOptions = []): void
{
$supportedDriverOptions = [
MYSQLI_OPT_CONNECT_TIMEOUT,
MYSQLI_OPT_LOCAL_INFILE,
MYSQLI_INIT_COMMAND,
MYSQLI_READ_DEFAULT_FILE,
MYSQLI_READ_DEFAULT_GROUP,
];
if (defined('MYSQLI_SERVER_PUBLIC_KEY')) {
$supportedDriverOptions[] = MYSQLI_SERVER_PUBLIC_KEY;
}
$exceptionMsg = "%s option '%s' with value '%s'";
foreach ($driverOptions as $option => $value) {
if ($option === static::OPTION_FLAGS) {
continue;
}
if (! in_array($option, $supportedDriverOptions, true)) {
throw new MysqliException(
sprintf($exceptionMsg, 'Unsupported', $option, $value)
);
}
if (@mysqli_options($this->conn, $option, $value)) {
continue;
}
throw ConnectionError::new($this->conn);
}
}
/**
* Pings the server and re-connects when `mysqli.reconnect = 1`
*
......@@ -221,32 +154,4 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
throw new MysqliException($this->conn->error, $this->conn->sqlstate, $this->conn->errno);
}
}
/**
* Establish a secure connection
*
* @param array<string, mixed> $params
*
* @throws MysqliException
*/
private function setSecureConnection(array $params): void
{
if (
! isset($params['ssl_key']) &&
! isset($params['ssl_cert']) &&
! isset($params['ssl_ca']) &&
! isset($params['ssl_capath']) &&
! isset($params['ssl_cipher'])
) {
return;
}
$this->conn->ssl_set(
$params['ssl_key'] ?? null,
$params['ssl_cert'] ?? null,
$params['ssl_ca'] ?? null,
$params['ssl_capath'] ?? null,
$params['ssl_cipher'] ?? null
);
}
}
......@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection;
......@@ -18,24 +17,16 @@ final class Driver extends AbstractOracleDriver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
try {
public function connect(array $params): Connection
{
return new OCI8Connection(
$username,
$password,
$params['user'] ?? '',
$params['password'] ?? '',
$this->constructDsn($params),
$params['charset'] ?? '',
$params['sessionMode'] ?? OCI_NO_AUTO_COMMIT,
$params['persistent'] ?? false
);
} catch (OCI8Exception $e) {
throw DBALException::driverException($this, $e);
}
}
/**
......
......@@ -38,7 +38,11 @@ class PDOConnection implements ServerInfoAwareConnection
public function exec(string $statement): int
{
try {
return $this->connection->exec($statement);
$result = $this->connection->exec($statement);
assert($result !== false);
return $result;
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
......
......@@ -4,11 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOMySql;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\PDOException;
use PDO;
/**
......@@ -19,28 +17,20 @@ final class Driver extends AbstractMySQLDriver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
public function connect(array $params): Connection
{
$driverOptions = $params['driver_options'] ?? [];
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
try {
$conn = new PDOConnection(
return new PDOConnection(
$this->constructPdoDsn($params),
$username,
$password,
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions
);
} catch (PDOException $e) {
throw DBALException::driverException($this, $e);
}
return $conn;
}
/**
......
......@@ -4,11 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOOracle;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\PDOException;
use PDO;
/**
......@@ -24,26 +22,20 @@ final class Driver extends AbstractOracleDriver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
public function connect(array $params): Connection
{
$driverOptions = $params['driver_options'] ?? [];
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
try {
return new PDOConnection(
$this->constructPdoDsn($params),
$username,
$password,
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions
);
} catch (PDOException $e) {
throw DBALException::driverException($this, $e);
}
}
/**
......
......@@ -4,11 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOPgSql;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\PDOException;
use PDO;
use function defined;
......@@ -21,22 +19,19 @@ final class Driver extends AbstractPostgreSQLDriver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
public function connect(array $params): Connection
{
$driverOptions = $params['driver_options'] ?? [];
if (! empty($params['persistent'])) {
$driverOptions[PDO::ATTR_PERSISTENT] = true;
}
try {
$connection = new PDOConnection(
$this->constructPdoDsn($params),
$username,
$password,
$driverOptions
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions,
);
if (
......@@ -57,9 +52,6 @@ final class Driver extends AbstractPostgreSQLDriver
}
return $connection;
} catch (PDOException $e) {
throw DBALException::driverException($this, $e);
}
}
/**
......
......@@ -4,11 +4,9 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOSqlite;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractSQLiteDriver;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\PDOException;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use function array_merge;
......@@ -28,12 +26,10 @@ final class Driver extends AbstractSQLiteDriver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
public function connect(array $params): Connection
{
$driverOptions = $params['driver_options'] ?? [];
if (isset($driverOptions['userDefinedFunctions'])) {
$this->userDefinedFunctions = array_merge(
$this->userDefinedFunctions,
......@@ -42,16 +38,12 @@ final class Driver extends AbstractSQLiteDriver
unset($driverOptions['userDefinedFunctions']);
}
try {
$connection = new PDOConnection(
$this->constructPdoDsn($params),
$username,
$password,
$params['user'] ?? '',
$params['password'] ?? '',
$driverOptions
);
} catch (PDOException $ex) {
throw DBALException::driverException($this, $ex);
}
$pdo = $connection->getWrappedConnection();
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver\PortWithoutHost;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use PDO;
......@@ -19,21 +20,19 @@ final class Driver extends AbstractSQLServerDriver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): DriverConnection {
public function connect(array $params): DriverConnection
{
$pdoOptions = $dsnOptions = [];
foreach ($driverOptions as $option => $value) {
if (isset($params['driver_options'])) {
foreach ($params['driver_options'] as $option => $value) {
if (is_int($option)) {
$pdoOptions[$option] = $value;
} else {
$dsnOptions[$option] = $value;
}
}
}
if (! empty($params['persistent'])) {
$pdoOptions[PDO::ATTR_PERSISTENT] = true;
......@@ -41,8 +40,8 @@ final class Driver extends AbstractSQLServerDriver
return new Connection(
$this->constructPdoDsn($params, $dsnOptions),
$username,
$password,
$params['user'] ?? '',
$params['password'] ?? '',
$pdoOptions
);
}
......@@ -61,11 +60,13 @@ final class Driver extends AbstractSQLServerDriver
if (isset($params['host'])) {
$dsn .= $params['host'];
}
if (isset($params['port'])) {
$dsn .= ',' . $params['port'];
}
} elseif (isset($params['port'])) {
throw PortWithoutHost::new();
}
if (isset($params['dbname'])) {
$connectionOptions['Database'] = $params['dbname'];
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractSQLAnywhereDriver;
use Doctrine\DBAL\Driver\Connection;
use function array_keys;
use function array_map;
use function array_merge;
use function implode;
use function sprintf;
/**
* A Doctrine DBAL driver for the SAP Sybase SQL Anywhere PHP extension.
*/
final class Driver extends AbstractSQLAnywhereDriver
{
/**
* {@inheritdoc}
*
* @throws DBALException If there was a problem establishing the connection.
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
try {
return new SQLAnywhereConnection(
$this->buildDsn($params, $username, $password, $driverOptions),
$params['persistent'] ?? false
);
} catch (SQLAnywhereException $e) {
throw DBALException::driverException($this, $e);
}
}
/**
* Build the connection string for given connection parameters and driver options.
*
* @param mixed[] $params DBAL connection parameters
* @param string $username User name to use for connection authentication.
* @param string $password Password to use for connection authentication.
* @param mixed[] $driverOptions Additional parameters to use for the connection.
*/
private function buildDsn(array $params, string $username, string $password, array $driverOptions = []): string
{
$connectionParams = [];
if (isset($params['host'])) {
$host = $params['host'];
if (isset($params['port'])) {
$host .= sprintf(':%d', $params['port']);
}
$connectionParams['HOST'] = $host;
}
if (isset($params['server'])) {
$connectionParams['ServerName'] = $params['server'];
}
if (isset($params['dbname'])) {
$connectionParams['DBN'] = $params['dbname'];
}
$connectionParams['UID'] = $username;
$connectionParams['PWD'] = $password;
$connectionParams = array_merge($connectionParams, $driverOptions);
return implode(';', array_map(static function (string $key, string $value): string {
return sprintf('%s=%s', $key, $value);
}, array_keys($connectionParams), $connectionParams));
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use function sasql_fetch_assoc;
use function sasql_fetch_row;
use function sasql_stmt_affected_rows;
use function sasql_stmt_field_count;
use function sasql_stmt_reset;
use function sasql_stmt_result_metadata;
final class Result implements ResultInterface
{
/** @var resource */
private $statement;
/** @var resource */
private $result;
/**
* @param resource $statement
*/
public function __construct($statement)
{
$this->statement = $statement;
$this->result = sasql_stmt_result_metadata($statement);
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
return sasql_fetch_row($this->result);
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
return sasql_fetch_assoc($this->result);
}
/**
* {@inheritDoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllNumeric(): array
{
return FetchUtils::fetchAllNumeric($this);
}
/**
* {@inheritDoc}
*/
public function fetchAllAssociative(): array
{
return FetchUtils::fetchAllAssociative($this);
}
/**
* {@inheritDoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
public function rowCount(): int
{
return sasql_stmt_affected_rows($this->statement);
}
public function columnCount(): int
{
return sasql_stmt_field_count($this->statement);
}
public function free(): void
{
sasql_stmt_reset($this->statement);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement;
use function assert;
use function is_resource;
use function is_string;
use function sasql_affected_rows;
use function sasql_commit;
use function sasql_connect;
use function sasql_escape_string;
use function sasql_insert_id;
use function sasql_pconnect;
use function sasql_real_query;
use function sasql_rollback;
use function sasql_set_option;
/**
* SAP Sybase SQL Anywhere implementation of the Connection interface.
*/
final class SQLAnywhereConnection implements ServerInfoAwareConnection
{
/** @var resource The SQL Anywhere connection resource. */
private $connection;
/**
* Connects to database with given connection string.
*
* @param string $dsn The connection string.
* @param bool $persistent Whether or not to establish a persistent connection.
*
* @throws SQLAnywhereException
*/
public function __construct(string $dsn, bool $persistent = false)
{
$this->connection = $persistent ? @sasql_pconnect($dsn) : @sasql_connect($dsn);
if (! is_resource($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError();
}
// Disable PHP warnings on error.
if (! sasql_set_option($this->connection, 'verbose_errors', false)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
// Enable auto committing by default.
if (! sasql_set_option($this->connection, 'auto_commit', 'on')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function beginTransaction(): void
{
if (! sasql_set_option($this->connection, 'auto_commit', 'off')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function commit(): void
{
if (! sasql_commit($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
$this->endTransaction();
}
public function exec(string $statement): int
{
if (sasql_real_query($this->connection, $statement) === false) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
return sasql_affected_rows($this->connection);
}
public function getServerVersion(): string
{
$version = $this->query("SELECT PROPERTY('ProductVersion')")->fetchOne();
assert(is_string($version));
return $version;
}
public function lastInsertId(?string $name = null): string
{
if ($name === null) {
return sasql_insert_id($this->connection);
}
return $this->query('SELECT ' . $name . '.CURRVAL')->fetchOne();
}
public function prepare(string $sql): Statement
{
return new SQLAnywhereStatement($this->connection, $sql);
}
public function query(string $sql): ResultInterface
{
return $this->prepare($sql)->execute();
}
public function quote(string $input): string
{
return "'" . sasql_escape_string($this->connection, $input) . "'";
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function rollBack(): void
{
if (! sasql_rollback($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
$this->endTransaction();
}
/**
* Ends transactional mode and enables auto commit again.
*
* @throws SQLAnywhereException
*/
private function endTransaction(): void
{
if (! sasql_set_option($this->connection, 'auto_commit', 'on')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\AbstractDriverException;
use function sasql_error;
use function sasql_errorcode;
use function sasql_sqlstate;
use function sasql_stmt_errno;
use function sasql_stmt_error;
/**
* SAP Sybase SQL Anywhere driver exception.
*
* @psalm-immutable
*/
class SQLAnywhereException extends AbstractDriverException
{
/**
* Helper method to turn SQL Anywhere error into exception.
*
* @param resource|null $conn The SQL Anywhere connection resource to retrieve the last error from.
* @param resource|null $stmt The SQL Anywhere statement resource to retrieve the last error from.
*/
public static function fromSQLAnywhereError($conn = null, $stmt = null): self
{
$state = $conn !== null ? sasql_sqlstate($conn) : sasql_sqlstate();
$code = 0;
$message = null;
/**
* Try retrieving the last error from statement resource if given
*/
if ($stmt !== null) {
$code = sasql_stmt_errno($stmt);
$message = sasql_stmt_error($stmt);
}
/**
* Try retrieving the last error from the connection resource
* if either the statement resource is not given or the statement
* resource is given but the last error could not be retrieved from it (fallback).
* Depending on the type of error, it is sometimes necessary to retrieve
* it from the connection resource even though it occurred during
* a prepared statement.
*/
if ($conn !== null && $code === 0) {
$code = sasql_errorcode($conn);
$message = sasql_error($conn);
}
/**
* Fallback mode if either no connection resource is given
* or the last error could not be retrieved from the given
* connection / statement resource.
*/
if ($conn === null || $code === 0) {
$code = sasql_errorcode();
$message = sasql_error();
}
if ($message) {
return new self('SQLSTATE [' . $state . '] [' . $code . '] ' . $message, $state, $code);
}
return new self('SQL Anywhere error occurred but no error message was retrieved from driver.', $state, $code);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Exception\GetVariableType;
use Doctrine\DBAL\ParameterType;
use function array_key_exists;
use function assert;
use function is_int;
use function is_resource;
use function sasql_prepare;
use function sasql_stmt_bind_param_ex;
use function sasql_stmt_execute;
use function sprintf;
/**
* SAP SQL Anywhere implementation of the Statement interface.
*/
final class SQLAnywhereStatement implements Statement
{
/** @var resource The connection resource. */
private $conn;
/** @var resource The prepared SQL statement to execute. */
private $stmt;
/** @var mixed[] The references to bound parameter values. */
private $boundValues = [];
/**
* Prepares given statement for given connection.
*
* @param resource $conn The connection resource to use.
* @param string $sql The SQL statement to prepare.
*
* @throws SQLAnywhereException
*/
public function __construct($conn, string $sql)
{
if (! is_resource($conn)) {
throw new SQLAnywhereException(sprintf(
'Invalid SQL Anywhere connection resource, %s given.',
(new GetVariableType())->__invoke($conn)
));
}
$this->conn = $conn;
$this->stmt = sasql_prepare($conn, $sql);
if (! is_resource($this->stmt)) {
throw SQLAnywhereException::fromSQLAnywhereError($conn);
}
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function bindParam($param, &$variable, int $type = ParameterType::STRING, ?int $length = null): void
{
assert(is_int($param));
switch ($type) {
case ParameterType::INTEGER:
case ParameterType::BOOLEAN:
$type = 'i';
break;
case ParameterType::LARGE_OBJECT:
$type = 'b';
break;
case ParameterType::NULL:
case ParameterType::STRING:
case ParameterType::BINARY:
$type = 's';
break;
default:
throw new SQLAnywhereException(sprintf('Unknown type %d.', $type));
}
$this->boundValues[$param] =& $variable;
if (! sasql_stmt_bind_param_ex($this->stmt, $param - 1, $variable, $type, $variable === null)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
}
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, int $type = ParameterType::STRING): void
{
$this->bindParam($param, $value, $type);
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function execute(?array $params = null): ResultInterface
{
if ($params !== null) {
$hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) {
if ($hasZeroIndex && is_int($key)) {
$this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
}
}
if (! sasql_stmt_execute($this->stmt)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
}
return new Result($this->stmt);
}
}
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver\PortWithoutHost;
use Doctrine\DBAL\Driver\Connection;
/**
......@@ -15,20 +16,21 @@ final class Driver extends AbstractSQLServerDriver
/**
* {@inheritdoc}
*/
public function connect(
array $params,
string $username = '',
string $password = '',
array $driverOptions = []
): Connection {
if (! isset($params['host'])) {
throw new SQLSrvException('Missing "host" in configuration for sqlsrv driver.');
}
public function connect(array $params): Connection
{
$serverName = '';
if (isset($params['host'])) {
$serverName = $params['host'];
if (isset($params['port'])) {
$serverName .= ', ' . $params['port'];
$serverName .= ',' . $params['port'];
}
} elseif (isset($params['port'])) {
throw PortWithoutHost::new();
}
$driverOptions = $params['driver_options'] ?? [];
if (isset($params['dbname'])) {
$driverOptions['Database'] = $params['dbname'];
......@@ -38,12 +40,12 @@ final class Driver extends AbstractSQLServerDriver
$driverOptions['CharacterSet'] = $params['charset'];
}
if ($username !== '') {
$driverOptions['UID'] = $username;
if (isset($params['user'])) {
$driverOptions['UID'] = $params['user'];
}
if ($password !== '') {
$driverOptions['PWD'] = $password;
if (isset($params['password'])) {
$driverOptions['PWD'] = $params['password'];
}
if (! isset($driverOptions['ReturnDatesAsStrings'])) {
......
......@@ -13,7 +13,6 @@ use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOCIDriver;
use Doctrine\DBAL\Driver\PDOPgSql\Driver as PDOPgSQLDriver;
use Doctrine\DBAL\Driver\PDOSqlite\Driver as PDOSQLiteDriver;
use Doctrine\DBAL\Driver\PDOSqlsrv\Driver as PDOSQLSrvDriver;
use Doctrine\DBAL\Driver\SQLAnywhere\Driver as SQLAnywhereDriver;
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
use Doctrine\DBAL\Exception\DriverRequired;
use Doctrine\DBAL\Exception\InvalidDriverClass;
......@@ -57,7 +56,6 @@ final class DriverManager
'ibm_db2' => DB2Driver::class,
'pdo_sqlsrv' => PDOSQLSrvDriver::class,
'mysqli' => MySQLiDriver::class,
'sqlanywhere' => SQLAnywhereDriver::class,
'sqlsrv' => SQLSrvDriver::class,
];
......@@ -145,14 +143,14 @@ final class DriverManager
$params = self::parseDatabaseUrl($params);
// URL support for MasterSlaveConnection
if (isset($params['master'])) {
$params['master'] = self::parseDatabaseUrl($params['master']);
// URL support for PrimaryReplicaConnection
if (isset($params['primary'])) {
$params['primary'] = self::parseDatabaseUrl($params['primary']);
}
if (isset($params['slaves'])) {
foreach ($params['slaves'] as $key => $slaveParams) {
$params['slaves'][$key] = self::parseDatabaseUrl($slaveParams);
if (isset($params['replica'])) {
foreach ($params['replica'] as $key => $replicaParams) {
$params['replica'][$key] = self::parseDatabaseUrl($replicaParams);
}
}
......
......@@ -6,6 +6,7 @@ namespace Doctrine\DBAL\Id;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\LockMode;
use Throwable;
......@@ -70,12 +71,16 @@ class TableGenerator
*/
public function __construct(Connection $conn, string $generatorTableName = 'sequences')
{
$params = $conn->getParams();
if ($params['driver'] === 'pdo_sqlite') {
if ($conn->getDriver() instanceof Driver\PDOSqlite\Driver) {
throw new DBALException('Cannot use TableGenerator with SQLite.');
}
$this->conn = DriverManager::getConnection($params, $conn->getConfiguration(), $conn->getEventManager());
$this->conn = DriverManager::getConnection(
$conn->getParams(),
$conn->getConfiguration(),
$conn->getEventManager()
);
$this->generatorTableName = $generatorTableName;
}
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Platforms\Keywords;
/**
* SAP Sybase SQL Anywhere 16 reserved keywords list.
*/
class SQLAnywhere16Keywords extends KeywordList
{
public function getName(): string
{
return 'SQLAnywhere16';
}
/**
* {@inheritdoc}
*
* @link http://infocenter.sybase.com/help/topic/com.sybase.dbrfen10/pdf/dbrfen10.pdf?noframes=true
*/
protected function getKeywords(): array
{
return [
'ADD',
'ALL',
'ALTER',
'AND',
'ANY',
'ARRAY',
'AS',
'ASC',
'ATTACH',
'BACKUP',
'BEGIN',
'BETWEEN',
'BIGINT',
'BINARY',
'BIT',
'BOTTOM',
'BREAK',
'BY',
'CALL',
'CAPABILITY',
'CASCADE',
'CASE',
'CAST',
'CHAR',
'CHAR_CONVERT',
'CHARACTER',
'CHECK',
'CHECKPOINT',
'CLOSE',
'COMMENT',
'COMMIT',
'COMPRESSED',
'CONFLICT',
'CONNECT',
'CONSTRAINT',
'CONTAINS',
'CONTINUE',
'CONVERT',
'CREATE',
'CROSS',
'CUBE',
'CURRENT',
'CURRENT_TIMESTAMP',
'CURRENT_USER',
'CURSOR',
'DATE',
'DATETIMEOFFSET',
'DBSPACE',
'DEALLOCATE',
'DEC',
'DECIMAL',
'DECLARE',
'DEFAULT',
'DELETE',
'DELETING',
'DESC',
'DETACH',
'DISTINCT',
'DO',
'DOUBLE',
'DROP',
'DYNAMIC',
'ELSE',
'ELSEIF',
'ENCRYPTED',
'END',
'ENDIF',
'ESCAPE',
'EXCEPT',
'EXCEPTION',
'EXEC',
'EXECUTE',
'EXISTING',
'EXISTS',
'EXTERNLOGIN',
'FETCH',
'FIRST',
'FLOAT',
'FOR',
'FORCE',
'FOREIGN',
'FORWARD',
'FROM',
'FULL',
'GOTO',
'GRANT',
'GROUP',
'HAVING',
'HOLDLOCK',
'IDENTIFIED',
'IF',
'IN',
'INDEX',
'INNER',
'INOUT',
'INSENSITIVE',
'INSERT',
'INSERTING',
'INSTALL',
'INSTEAD',
'INT',
'INTEGER',
'INTEGRATED',
'INTERSECT',
'INTO',
'IS',
'ISOLATION',
'JOIN',
'JSON',
'KERBEROS',
'KEY',
'LATERAL',
'LEFT',
'LIKE',
'LIMIT',
'LOCK',
'LOGIN',
'LONG',
'MATCH',
'MEMBERSHIP',
'MERGE',
'MESSAGE',
'MODE',
'MODIFY',
'NATURAL',
'NCHAR',
'NEW',
'NO',
'NOHOLDLOCK',
'NOT',
'NOTIFY',
'NULL',
'NUMERIC',
'NVARCHAR',
'OF',
'OFF',
'ON',
'OPEN',
'OPENSTRING',
'OPENXML',
'OPTION',
'OPTIONS',
'OR',
'ORDER',
'OTHERS',
'OUT',
'OUTER',
'OVER',
'PASSTHROUGH',
'PRECISION',
'PREPARE',
'PRIMARY',
'PRINT',
'PRIVILEGES',
'PROC',
'PROCEDURE',
'PUBLICATION',
'RAISERROR',
'READTEXT',
'REAL',
'REFERENCE',
'REFERENCES',
'REFRESH',
'RELEASE',
'REMOTE',
'REMOVE',
'RENAME',
'REORGANIZE',
'RESOURCE',
'RESTORE',
'RESTRICT',
'RETURN',
'REVOKE',
'RIGHT',
'ROLLBACK',
'ROLLUP',
'ROW',
'ROWTYPE',
'SAVE',
'SAVEPOINT',
'SCROLL',
'SELECT',
'SENSITIVE',
'SESSION',
'SET',
'SETUSER',
'SHARE',
'SMALLINT',
'SOME',
'SPATIAL',
'SQLCODE',
'SQLSTATE',
'START',
'STOP',
'SUBTRANS',
'SUBTRANSACTION',
'SYNCHRONIZE',
'TABLE',
'TEMPORARY',
'THEN',
'TIME',
'TIMESTAMP',
'TINYINT',
'TO',
'TOP',
'TRAN',
'TREAT',
'TRIGGER',
'TRUNCATE',
'TSEQUAL',
'UNBOUNDED',
'UNION',
'UNIQUE',
'UNIQUEIDENTIFIER',
'UNKNOWN',
'UNNEST',
'UNSIGNED',
'UPDATE',
'UPDATING',
'USER',
'USING',
'VALIDATE',
'VALUES',
'VARBINARY',
'VARBIT',
'VARCHAR',
'VARIABLE',
'VARRAY',
'VARYING',
'VIEW',
'WAIT',
'WAITFOR',
'WHEN',
'WHERE',
'WHILE',
'WINDOW',
'WITH',
'WITHIN',
'WORK',
'WRITETEXT',
'XML',
];
}
}
This diff is collapsed.
......@@ -4,9 +4,13 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Portability;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Abstraction\Result as AbstractionResult;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\ColumnCase;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection as BaseConnection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
......@@ -19,7 +23,7 @@ use const CASE_UPPER;
/**
* Portability wrapper for a Connection.
*/
class Connection extends \Doctrine\DBAL\Connection
class Connection extends BaseConnection
{
public const PORTABILITY_ALL = 255;
public const PORTABILITY_NONE = 0;
......@@ -27,17 +31,35 @@ class Connection extends \Doctrine\DBAL\Connection
public const PORTABILITY_EMPTY_TO_NULL = 4;
public const PORTABILITY_FIX_CASE = 8;
public const PORTABILITY_DB2 = 13;
public const PORTABILITY_ORACLE = 9;
public const PORTABILITY_POSTGRESQL = 13;
public const PORTABILITY_SQLITE = 13;
public const PORTABILITY_OTHERVENDORS = 12;
public const PORTABILITY_SQLANYWHERE = 13;
public const PORTABILITY_SQLSRV = 13;
/** @var int */
private $portability = self::PORTABILITY_NONE;
/** @var int */
private $case = 0;
/** @var Converter */
private $converter;
/** {@inheritDoc} */
public function __construct(
array $params,
Driver $driver,
?Configuration $config = null,
?EventManager $eventManager = null
) {
if (isset($params['portability'])) {
$this->portability = $params['portability'];
}
if (isset($params['fetch_case'])) {
$this->case = $params['fetch_case'];
}
unset($params['portability'], $params['fetch_case']);
parent::__construct($params, $driver, $config, $eventManager);
}
public function connect(): void
{
if ($this->isConnected()) {
......@@ -46,35 +68,19 @@ class Connection extends \Doctrine\DBAL\Connection
parent::connect();
$params = $this->getParams();
$portability = self::PORTABILITY_NONE;
if (isset($params['portability'])) {
if ($this->getDatabasePlatform()->getName() === 'oracle') {
$portability = $params['portability'] & self::PORTABILITY_ORACLE;
} elseif ($this->getDatabasePlatform()->getName() === 'postgresql') {
$portability = $params['portability'] & self::PORTABILITY_POSTGRESQL;
} elseif ($this->getDatabasePlatform()->getName() === 'sqlite') {
$portability = $params['portability'] & self::PORTABILITY_SQLITE;
} elseif ($this->getDatabasePlatform()->getName() === 'sqlanywhere') {
$portability = $params['portability'] & self::PORTABILITY_SQLANYWHERE;
} elseif ($this->getDatabasePlatform()->getName() === 'db2') {
$portability = $params['portability'] & self::PORTABILITY_DB2;
} elseif ($this->getDatabasePlatform()->getName() === 'mssql') {
$portability = $params['portability'] & self::PORTABILITY_SQLSRV;
} else {
$portability = $params['portability'] & self::PORTABILITY_OTHERVENDORS;
}
}
$portability = (new OptimizeFlags())(
$this->getDatabasePlatform(),
$this->portability
);
$case = null;
$case = 0;
if (isset($params['fetch_case']) && ($portability & self::PORTABILITY_FIX_CASE) !== 0) {
if ($this->case !== 0 && ($portability & self::PORTABILITY_FIX_CASE) !== 0) {
if ($this->_conn instanceof PDOConnection) {
// make use of c-level support for case handling
$this->_conn->getWrappedConnection()->setAttribute(PDO::ATTR_CASE, $params['fetch_case']);
$this->_conn->getWrappedConnection()->setAttribute(PDO::ATTR_CASE, $this->case);
} else {
$case = $params['fetch_case'] === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER;
$case = $this->case === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER;
}
}
......@@ -99,6 +105,9 @@ class Connection extends \Doctrine\DBAL\Connection
);
}
/**
* @return Statement
*/
public function prepare(string $sql): DriverStatement
{
return new Statement(parent::prepare($sql), $this->converter);
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Portability;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Platforms\SQLServer2012Platform;
final class OptimizeFlags
{
/**
* Platform-specific portability flags that need to be excluded from the user-provided mode
* since the platform already operates in this mode to avoid unnecessary conversion overhead.
*
* @var array<string,int>
*/
private static $platforms = [
DB2Platform::class => 0,
OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL,
PostgreSQL94Platform::class => 0,
SqlitePlatform::class => 0,
SQLServer2012Platform::class => 0,
];
public function __invoke(AbstractPlatform $platform, int $flags): int
{
foreach (self::$platforms as $class => $mask) {
if ($platform instanceof $class) {
$flags &= ~$mask;
break;
}
}
return $flags;
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\SQLAnywhere16Platform;
use Doctrine\DBAL\Types\Type;
use function assert;
use function is_string;
use function preg_replace;
/**
* SAP Sybase SQL Anywhere schema manager.
*/
class SQLAnywhereSchemaManager extends AbstractSchemaManager
{
/**
* {@inheritdoc}
*
* Starts a database after creation
* as SQL Anywhere needs a database to be started
* before it can be used.
*
* @see startDatabase
*/
public function createDatabase(string $database): void
{
parent::createDatabase($database);
$this->startDatabase($database);
}
/**
* {@inheritdoc}
*
* Tries stopping a database before dropping
* as SQL Anywhere needs a database to be stopped
* before it can be dropped.
*
* @see stopDatabase
*/
public function dropDatabase(string $database): void
{
$this->tryMethod('stopDatabase', $database);
parent::dropDatabase($database);
}
public function startDatabase(string $database): void
{
assert($this->_platform instanceof SQLAnywhere16Platform);
$this->_execSql($this->_platform->getStartDatabaseSQL($database));
}
public function stopDatabase(string $database): void
{
assert($this->_platform instanceof SQLAnywhere16Platform);
$this->_execSql($this->_platform->getStopDatabaseSQL($database));
}
/**
* {@inheritdoc}
*/
protected function _getPortableDatabaseDefinition(array $database): string
{
return $database['name'];
}
/**
* {@inheritdoc}
*/
protected function _getPortableSequenceDefinition(array $sequence): Sequence
{
return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['start_with']);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableColumnDefinition(array $tableColumn): Column
{
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'])
?? $this->_platform->getDoctrineTypeMapping($tableColumn['type']);
$precision = null;
$scale = null;
$fixed = false;
$default = null;
if ($tableColumn['default'] !== null) {
// Strip quotes from default value.
$default = preg_replace(["/^'(.*)'$/", "/''/"], ['$1', "'"], $tableColumn['default']);
if ($default === 'autoincrement') {
$default = null;
}
}
switch ($tableColumn['type']) {
case 'binary':
case 'char':
case 'nchar':
$fixed = true;
break;
}
switch ($type) {
case 'decimal':
case 'float':
$precision = $tableColumn['length'];
$scale = $tableColumn['scale'];
break;
}
return new Column(
$tableColumn['column_name'],
Type::getType($type),
[
'length' => $type === 'string' ? $tableColumn['length'] : null,
'precision' => $precision,
'scale' => $scale,
'unsigned' => (bool) $tableColumn['unsigned'],
'fixed' => $fixed,
'notnull' => (bool) $tableColumn['notnull'],
'default' => $default,
'autoincrement' => (bool) $tableColumn['autoincrement'],
'comment' => $tableColumn['comment'] ?? '',
]
);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableDefinition(array $table): string
{
return $table['table_name'];
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint
{
return new ForeignKeyConstraint(
$tableForeignKey['local_columns'],
$tableForeignKey['foreign_table'],
$tableForeignKey['foreign_columns'],
$tableForeignKey['name'],
$tableForeignKey['options']
);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array
{
$foreignKeys = [];
foreach ($tableForeignKeys as $tableForeignKey) {
if (! isset($foreignKeys[$tableForeignKey['index_name']])) {
$foreignKeys[$tableForeignKey['index_name']] = [
'local_columns' => [$tableForeignKey['local_column']],
'foreign_table' => $tableForeignKey['foreign_table'],
'foreign_columns' => [$tableForeignKey['foreign_column']],
'name' => $tableForeignKey['index_name'],
'options' => [
'notnull' => $tableForeignKey['notnull'],
'match' => $tableForeignKey['match'],
'onUpdate' => $tableForeignKey['on_update'],
'onDelete' => $tableForeignKey['on_delete'],
'check_on_commit' => $tableForeignKey['check_on_commit'],
'clustered' => $tableForeignKey['clustered'],
'for_olap_workload' => $tableForeignKey['for_olap_workload'],
],
];
} else {
$foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column'];
$foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column'];
}
}
return parent::_getPortableTableForeignKeysList($foreignKeys);
}
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList(array $tableIndexRows, string $tableName): array
{
foreach ($tableIndexRows as &$tableIndex) {
$tableIndex['primary'] = (bool) $tableIndex['primary'];
$tableIndex['flags'] = [];
if ($tableIndex['clustered']) {
$tableIndex['flags'][] = 'clustered';
}
if ($tableIndex['with_nulls_not_distinct']) {
$tableIndex['flags'][] = 'with_nulls_not_distinct';
}
if (! $tableIndex['for_olap_workload']) {
continue;
}
$tableIndex['flags'][] = 'for_olap_workload';
}
return parent::_getPortableTableIndexesList($tableIndexRows, $tableName);
}
/**
* {@inheritdoc}
*/
protected function _getPortableViewDefinition(array $view): View
{
$definition = preg_replace('/^.*\s+as\s+SELECT(.*)/i', 'SELECT$1', $view['view_def']);
assert(is_string($definition));
return new View($view['table_name'], $definition);
}
}
......@@ -14,11 +14,9 @@ use Doctrine\DBAL\Platforms\Keywords\OracleKeywords;
use Doctrine\DBAL\Platforms\Keywords\PostgreSQL100Keywords;
use Doctrine\DBAL\Platforms\Keywords\PostgreSQL94Keywords;
use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator;
use Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords;
use Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords;
use Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords;
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
use Exception;
use InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
......@@ -31,9 +29,6 @@ use function count;
use function implode;
use function is_string;
use function sprintf;
use function trigger_error;
use const E_USER_DEPRECATED;
class ReservedWordsCommand extends Command
{
......@@ -47,23 +42,17 @@ class ReservedWordsCommand extends Command
'oracle' => OracleKeywords::class,
'pgsql' => PostgreSQL94Keywords::class,
'pgsql100' => PostgreSQL100Keywords::class,
'sqlanywhere' => SQLAnywhere16Keywords::class,
'sqlite' => SQLiteKeywords::class,
'sqlserver' => SQLServer2012Keywords::class,
];
/** @var ConnectionProvider|null */
/** @var ConnectionProvider */
private $connectionProvider;
public function __construct(?ConnectionProvider $connectionProvider = null)
public function __construct(ConnectionProvider $connectionProvider)
{
parent::__construct();
$this->connectionProvider = $connectionProvider;
if ($connectionProvider !== null) {
return;
}
@trigger_error('Not passing a connection provider as the first constructor argument is deprecated', E_USER_DEPRECATED);
}
/**
......@@ -92,8 +81,8 @@ class ReservedWordsCommand extends Command
Checks if the current database contains tables and columns
with names that are identifiers in this dialect or in other SQL dialects.
By default SQLite, MySQL, PostgreSQL, Microsoft SQL Server, Oracle
and SQL Anywhere keywords are checked:
By default SQLite, MySQL, PostgreSQL, Microsoft SQL Server and Oracle
keywords are checked:
<info>%command.full_name%</info>
......@@ -114,7 +103,6 @@ The following keyword lists are currently shipped with Doctrine:
* oracle
* sqlserver
* sqlserver2012
* sqlanywhere
* db2 (Not checked by default)
EOT
);
......@@ -172,14 +160,6 @@ EOT
$connectionName = $input->getOption('connection');
assert(is_string($connectionName) || $connectionName === null);
if ($this->connectionProvider === null) {
if ($connectionName !== null) {
throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.');
}
return $this->getHelper('db')->getConnection();
}
if ($connectionName !== null) {
return $this->connectionProvider->getConnection($connectionName);
}
......
......@@ -7,7 +7,6 @@ namespace Doctrine\DBAL\Tools\Console\Command;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
use Doctrine\DBAL\Tools\Dumper;
use Exception;
use LogicException;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
......@@ -21,9 +20,6 @@ use function is_bool;
use function is_numeric;
use function is_string;
use function stripos;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Task for executing arbitrary SQL that can come from a file or directly from
......@@ -31,18 +27,13 @@ use const E_USER_DEPRECATED;
*/
class RunSqlCommand extends Command
{
/** @var ConnectionProvider|null */
/** @var ConnectionProvider */
private $connectionProvider;
public function __construct(?ConnectionProvider $connectionProvider = null)
public function __construct(ConnectionProvider $connectionProvider)
{
parent::__construct();
$this->connectionProvider = $connectionProvider;
if ($connectionProvider !== null) {
return;
}
@trigger_error('Not passing a connection provider as the first constructor argument is deprecated', E_USER_DEPRECATED);
}
protected function configure(): void
......@@ -105,14 +96,6 @@ EOT
$connectionName = $input->getOption('connection');
assert(is_string($connectionName) || $connectionName === null);
if ($this->connectionProvider === null) {
if ($connectionName !== null) {
throw new Exception('Specifying a connection is only supported when a ConnectionProvider is used.');
}
return $this->getHelper('db')->getConnection();
}
if ($connectionName !== null) {
return $this->connectionProvider->getConnection($connectionName);
}
......
......@@ -4,72 +4,36 @@ declare(strict_types=1);
namespace Doctrine\DBAL\Tools\Console;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand;
use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use PackageVersions\Versions;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\HelperSet;
use TypeError;
use function sprintf;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Handles running the Console Tools inside Symfony Console context.
*/
class ConsoleRunner
{
/**
* Create a Symfony Console HelperSet
*
* @deprecated use a ConnectionProvider instead.
*/
public static function createHelperSet(Connection $connection): HelperSet
{
return new HelperSet([
'db' => new ConnectionHelper($connection),
]);
}
/**
* Runs console with the given connection provider or helperset (deprecated).
*
* @param ConnectionProvider|HelperSet $helperSetOrConnectionProvider
* @param array<int, Command> $commands
*/
public static function run($helperSetOrConnectionProvider, $commands = []): void
public static function run(ConnectionProvider $connectionProvider, array $commands = []): void
{
$cli = new Application('Doctrine Command Line Interface', Versions::getVersion('doctrine/dbal'));
$cli->setCatchExceptions(true);
$connectionProvider = null;
if ($helperSetOrConnectionProvider instanceof HelperSet) {
@trigger_error(sprintf('Passing an instance of "%s" as the first argument is deprecated. Pass an instance of "%s" instead.', HelperSet::class, ConnectionProvider::class), E_USER_DEPRECATED);
$connectionProvider = null;
$cli->setHelperSet($helperSetOrConnectionProvider);
} elseif ($helperSetOrConnectionProvider instanceof ConnectionProvider) {
$connectionProvider = $helperSetOrConnectionProvider;
} else {
throw new TypeError(sprintf('First argument must be an instance of "%s" or "%s"', HelperSet::class, ConnectionProvider::class));
}
self::addCommands($cli, $connectionProvider);
$cli->addCommands($commands);
$cli->run();
}
public static function addCommands(Application $cli, ?ConnectionProvider $connectionProvider = null): void
public static function addCommands(Application $cli, ConnectionProvider $connectionProvider): void
{
$cli->addCommands([
new RunSqlCommand(),
new ReservedWordsCommand(),
new RunSqlCommand($connectionProvider),
new ReservedWordsCommand($connectionProvider),
]);
}
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Tools\Console\Helper;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Helper\Helper;
/**
* Doctrine CLI Connection Helper.
*
* @deprecated use a ConnectionProvider instead.
*/
class ConnectionHelper extends Helper
{
/**
* The Doctrine database Connection.
*
* @var Connection
*/
protected $_connection;
/**
* @param Connection $connection The Doctrine database Connection.
*/
public function __construct(Connection $connection)
{
$this->_connection = $connection;
}
/**
* Retrieves the Doctrine database Connection.
*/
public function getConnection(): Connection
{
return $this->_connection;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'connection';
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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