Skip to content

Commit 2e5552e

Browse files
committed
Merge branch '3.6.x' into 4.0.x
* 3.6.x: Optimize pgsql queries without parameters Raise exception if pg_send_*() calls fail Add the PgSQL driver Make sure only PDO parameter types are passed [Docs] Add IBM DB2 to `configuration.rst` Use psalm-assert to get rid of assert() calls Introduce the SchemaManagerFactory interface
2 parents 8c327e2 + 0b23e06 commit 2e5552e

31 files changed

+1308
-61
lines changed

.github/workflows/continuous-integration.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,17 @@ jobs:
212212
- "8.1"
213213
postgres-version:
214214
- "10"
215-
- "14"
216215
- "15"
216+
extension:
217+
- "pgsql"
218+
- "pdo_pgsql"
217219
include:
218220
- php-version: "8.2"
219221
postgres-version: "15"
222+
extension: "pgsql"
223+
- php-version: "8.2"
224+
postgres-version: "15"
225+
extension: "pdo_pgsql"
220226

221227
services:
222228
postgres:
@@ -240,6 +246,7 @@ jobs:
240246
uses: "shivammathur/setup-php@v2"
241247
with:
242248
php-version: "${{ matrix.php-version }}"
249+
extensions: "pgsql pdo_pgsql"
243250
coverage: "pcov"
244251
ini-values: "zend.assertions=1"
245252

@@ -249,12 +256,12 @@ jobs:
249256
composer-options: "--ignore-platform-req=php+"
250257

251258
- name: "Run PHPUnit"
252-
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
259+
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
253260

254261
- name: "Upload coverage file"
255262
uses: "actions/upload-artifact@v3"
256263
with:
257-
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}.coverage"
264+
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.extension }}-${{ matrix.php-version }}.coverage"
258265
path: "coverage.xml"
259266

260267
phpunit-mariadb:

UPGRADE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,25 @@ The following methods have been removed.
882882

883883
# Upgrade to 3.6
884884

885+
## Deprecated not setting a schema manager factory
886+
887+
DBAL 4 will change the way the schema manager is created. To opt in to the new
888+
behavior, please configure the schema manager factory:
889+
890+
```php
891+
$configuration = new Configuration();
892+
$configuration->setSchemaManagerFactory(new DefaultSchemaManagerFactory());
893+
894+
$connection = DriverManager::getConnection(
895+
[/* your parameters */],
896+
$configuration,
897+
);
898+
```
899+
900+
If you use a custom platform implementation, please make sure it implements
901+
the `createSchemaManager()`method . Otherwise, the connection will fail to
902+
create a schema manager.
903+
885904
## Deprecated the `url` connection parameter
886905

887906
DBAL ships with a new and configurable DSN parser that can be used to parse a

ci/github/phpunit/pgsql.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="../../../vendor/phpunit/phpunit/phpunit.xsd"
4+
colors="true"
5+
beStrictAboutOutputDuringTests="true"
6+
beStrictAboutTodoAnnotatedTests="true"
7+
failOnRisky="true"
8+
failOnWarning="true"
9+
convertDeprecationsToExceptions="true"
10+
>
11+
<php>
12+
<ini name="error_reporting" value="-1" />
13+
14+
<var name="db_driver" value="pgsql"/>
15+
<var name="db_host" value="localhost" />
16+
<var name="db_user" value="postgres" />
17+
<var name="db_password" value="postgres" />
18+
<var name="db_dbname" value="doctrine_tests" />
19+
</php>
20+
21+
<testsuites>
22+
<testsuite name="Doctrine DBAL Test Suite">
23+
<directory>../../../tests</directory>
24+
</testsuite>
25+
</testsuites>
26+
27+
<coverage>
28+
<include>
29+
<directory suffix=".php">../../../src</directory>
30+
</include>
31+
</coverage>
32+
</phpunit>

docs/en/reference/configuration.rst

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,15 @@ interfaces to use. It can be configured in one of three ways:
151151
- ``sqlite3``: An SQLite driver that uses the sqlite3 extension.
152152
- ``pdo_pgsql``: A PostgreSQL driver that uses the pdo_pgsql PDO
153153
extension.
154+
- ``pgsql``: A PostgreSQL driver that uses the pgsql extension. This driver does support loading BLOBs from file
155+
resources yet. It is still considered experimental. Use `pdo_pgsql` if you need a stable driver for Postgres.
154156
- ``pdo_oci``: An Oracle driver that uses the pdo_oci PDO
155157
extension.
156158
**Note that this driver caused problems in our tests. Prefer the oci8 driver if possible.**
157159
- ``pdo_sqlsrv``: A Microsoft SQL Server driver that uses pdo_sqlsrv PDO
158160
- ``sqlsrv``: A Microsoft SQL Server driver that uses the sqlsrv PHP extension.
159161
- ``oci8``: An Oracle driver that uses the oci8 PHP extension.
162+
- ``ibm_db2``: An IBM DB2 driver that uses the ibm_db2 PHP extension.
160163

161164
- ``driverClass``: Specifies a custom driver implementation if no
162165
'driver' is specified. This allows the use of custom drivers that
@@ -235,8 +238,8 @@ mysqli
235238
- ``ssl_cipher`` (string): A list of allowable ciphers to use for SSL encryption.
236239
- ``driverOptions`` Any supported flags for mysqli found on `http://www.php.net/manual/en/mysqli.real-connect.php`
237240

238-
pdo_pgsql
239-
^^^^^^^^^
241+
pdo_pgsql / pgsql
242+
^^^^^^^^^^^^^^^^^
240243

241244
- ``user`` (string): Username to use when connecting to the
242245
database.
@@ -315,6 +318,18 @@ pdo_sqlsrv / sqlsrv
315318
- ``port`` (integer): Port of the database to connect to.
316319
- ``dbname`` (string): Name of the database/schema to connect to.
317320

321+
ibm_db2
322+
^^^^^^^
323+
324+
- ``dbname`` (string): Name of the database/schema to connect to or a complete connection string in
325+
the format "DATABASE=dbname;HOSTNAME=host;PORT=port;PROTOCOL=TCPIP;UID=user;PWD=password;".
326+
- ``user`` (string): Username to use when connecting to the database.
327+
- ``password`` (string): Password to use when connecting to the database.
328+
- ``host`` (string): Hostname of the database to connect to.
329+
- ``port`` (integer): Port of the database to connect to.
330+
- ``persistent`` (boolean): Whether to establish a persistent connection.
331+
- ``driverOptions`` (array): Any supported options found on `https://www.php.net/manual/en/function.db2-connect.php#refsect1-function.db2-connect-parameters`
332+
318333
Automatic platform version detection
319334
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
320335

docs/en/reference/schema-manager.rst

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Retrieve an array of databases on the configured connection:
3636
$databases = $sm->listDatabases();
3737
3838
listSequences()
39-
-------------------------------
39+
---------------
4040

4141
Retrieve an array of ``Doctrine\DBAL\Schema\Sequence`` instances
4242
that exist for a database:
@@ -63,7 +63,7 @@ Now you can loop over the array inspecting each sequence object:
6363
}
6464
6565
listTableColumns()
66-
----------------------------
66+
------------------
6767

6868
Retrieve an array of ``Doctrine\DBAL\Schema\Column`` instances that
6969
exist for the given table:
@@ -102,7 +102,7 @@ schema for that table. For example we can add a new column:
102102
$table->addColumn('email_address', 'string');
103103
104104
listTableForeignKeys()
105-
--------------------------------
105+
----------------------
106106

107107
Retrieve an array of ``Doctrine\DBAL\Schema\ForeignKeyConstraint``
108108
instances that exist for the given table:
@@ -123,7 +123,7 @@ object:
123123
}
124124
125125
listTableIndexes()
126-
----------------------------
126+
------------------
127127

128128
Retrieve an array of ``Doctrine\DBAL\Schema\Index`` instances that
129129
exist for the given table:
@@ -232,3 +232,50 @@ table:
232232
0 => 'DROP TABLE user'
233233
)
234234
*/
235+
236+
Overriding the schema manager
237+
-----------------------------
238+
239+
All schema manager classes can be overridden, for instance if your application needs to modify SQL statements emitted
240+
by the schema manager or the comparator. If you want your own schema manager to be returned by
241+
``Connection::createSchemaManager()`` you need to configure a factory for it.
242+
243+
.. code-block:: php
244+
245+
<?php
246+
use Doctrine\DBAL\Configuration;
247+
use Doctrine\DBAL\DriverManager;
248+
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
249+
use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
250+
use Doctrine\DBAL\Schema\MySQLSchemaManager;
251+
use Doctrine\DBAL\Schema\SchemaManagerFactory;
252+
253+
class MyCustomMySQLSchemaManager extends MySQLSchemaManager
254+
{
255+
// .. your custom logic.
256+
}
257+
258+
final class MySchemaManagerFactory implements SchemaManagerFactory
259+
{
260+
private readonly SchemaManagerFactory $defaultFactory;
261+
262+
public function __construct()
263+
{
264+
$this->defaultFactory = new DefaultSchemaManagerFactory();
265+
}
266+
267+
public function createSchemaManager(Connection $connection): AbstractSchemaManager
268+
{
269+
$platform = $connection->getDatabasePlatform();
270+
if ($platform instanceof AbstractMySQLPlatform) {
271+
return new MyCustomMySQLSchemaManager($connection, $platform);
272+
}
273+
274+
return $this->defaultFactory->createSchemaManager($connection);
275+
}
276+
}
277+
278+
$configuration = new Configuration();
279+
$configuration->setSchemaManagerFactory(new MySchemaManagerFactory());
280+
281+
$connection = DriverManager::getConnection([/* your connection parameters */], $configuration);

phpcs.xml.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@
5353
<exclude-pattern>*/tests/*</exclude-pattern>
5454
</rule>
5555

56+
<rule ref="SlevomatCodingStandard.Classes.RequireConstructorPropertyPromotion.RequiredConstructorPropertyPromotion">
57+
<!-- CPP breaks static analysis here. -->
58+
<exclude-pattern>src/Driver/PgSQL/Result.php</exclude-pattern>
59+
</rule>
60+
5661
<rule ref="SlevomatCodingStandard.PHP.RequireExplicitAssertion.RequiredExplicitAssertion">
5762
<exclude-pattern>tests/*</exclude-pattern>
5863
</rule>

phpstan.neon.dist

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ parameters:
6464
- src/Driver/Mysqli/Exception/StatementError.php
6565

6666
# We're testing with invalid input.
67-
-
68-
message: '~^Unable to resolve the template type T in call to method static method Doctrine\\DBAL\\DriverManager::getConnection\(\)~'
69-
path: tests/DriverManagerTest.php
7067
-
7168
message: '~array{driver: ''invalid_driver''} given\.$~'
7269
path: tests/DriverManagerTest.php
@@ -96,6 +93,25 @@ parameters:
9693
paths:
9794
- src/Schema/PostgreSQLSchemaManager.php
9895

96+
# We don't need to declare the return type *that* fine-grained.
97+
- '~^Method Doctrine\\DBAL\\Driver\\PDO\\Statement\:\:convertParamType\(\) never returns \d+ so it can be removed from the return type\.$~'
98+
99+
# PgSql objects are represented as resources in PHP 7.4
100+
- '~ expects PgSql\\Connection(:?\|(?:string|null))?, PgSql\\Connection\|resource given\.$~'
101+
- '~ expects PgSql\\Result, PgSql\\Result\|resource given\.$~'
102+
103+
# PHPStan does not understand the array shapes returned by pg_fetch_*() methods.
104+
- '~^Parameter #1 \$row of method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:mapAssociativeRow\(\) expects array<string, string\|null>, array<int\|string, string\|null> given\.$~'
105+
- '~^Parameter #1 \$row of method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:mapNumericRow\(\) expects array<int, string\|null>, array<int\|string, string\|null> given\.$~'
106+
107+
# Ignore isset() checks in destructors.
108+
- '~^Property Doctrine\\DBAL\\Driver\\PgSQL\\Connection\:\:\$connection \(PgSql\\Connection\|resource\) in isset\(\) is not nullable\.$~'
109+
110+
# On PHP 7.4, pg_fetch_all() might return false for empty result sets.
111+
-
112+
message: '~^Strict comparison using === between array<int, array> and false will always evaluate to false\.$~'
113+
paths:
114+
- src/Driver/PgSQL/Result.php
99115
includes:
100116
- vendor/phpstan/phpstan-phpunit/extension.neon
101117
- vendor/phpstan/phpstan-phpunit/rules.neon

psalm.xml.dist

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
<file name="vendor/jetbrains/phpstorm-stubs/ibm_db2/ibm_db2.php" />
2020
<file name="vendor/jetbrains/phpstorm-stubs/mysqli/mysqli.php" />
2121
<file name="vendor/jetbrains/phpstorm-stubs/oci8/oci8.php" />
22-
<file name="vendor/jetbrains/phpstorm-stubs/pgsql/pgsql.php" />
2322
<file name="vendor/jetbrains/phpstorm-stubs/sqlsrv/sqlsrv.php" />
2423
</stubs>
2524
<issueHandlers>
@@ -50,6 +49,7 @@
5049
-->
5150
<file name="src/Connection.php"/>
5251
<file name="src/Driver/IBMDB2/Statement.php"/>
52+
<directory name="src/Driver/PgSQL"/>
5353
<!--
5454
Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/1255
5555
-->
@@ -115,6 +115,30 @@
115115
<file name="src/Driver/AbstractSQLiteDriver.php"/>
116116
</errorLevel>
117117
</NullableReturnStatement>
118+
<PossiblyInvalidArgument>
119+
<errorLevel type="suppress">
120+
<!-- PgSql objects are represented as resources in PHP 7.4 -->
121+
<referencedFunction name="pg_affected_rows"/>
122+
<referencedFunction name="pg_close"/>
123+
<referencedFunction name="pg_escape_bytea"/>
124+
<referencedFunction name="pg_escape_literal"/>
125+
<referencedFunction name="pg_fetch_all"/>
126+
<referencedFunction name="pg_fetch_all_columns"/>
127+
<referencedFunction name="pg_fetch_assoc"/>
128+
<referencedFunction name="pg_fetch_row"/>
129+
<referencedFunction name="pg_field_name"/>
130+
<referencedFunction name="pg_field_type"/>
131+
<referencedFunction name="pg_free_result"/>
132+
<referencedFunction name="pg_get_result"/>
133+
<referencedFunction name="pg_last_error"/>
134+
<referencedFunction name="pg_num_fields"/>
135+
<referencedFunction name="pg_result_error_field"/>
136+
<referencedFunction name="pg_send_execute"/>
137+
<referencedFunction name="pg_send_prepare"/>
138+
<referencedFunction name="pg_send_query"/>
139+
<referencedFunction name="pg_version"/>
140+
</errorLevel>
141+
</PossiblyInvalidArgument>
118142
<PossiblyInvalidArrayOffset>
119143
<errorLevel type="suppress">
120144
<!-- $array[key($array)] is safe. -->
@@ -168,11 +192,14 @@
168192
<file name="src/Platforms/AbstractMySQLPlatform.php"/>
169193
<file name="tests/Functional/Driver/AbstractDriverTest.php"/>
170194

195+
<!-- We're checking for invalid input. -->
196+
<directory name="src/Driver/PgSQL"/>
197+
171198
<!-- We're testing invalid input. -->
172199
<file name="tests/Types/DateImmutableTypeTest.php"/>
173200
<file name="tests/Types/DateTimeImmutableTypeTest.php"/>
174201
<file name="tests/Types/TimeImmutableTypeTest.php"/>
175-
202+
176203
<!-- False positive: "mixed is never string" -->
177204
<file name="src/Platforms/PostgreSQLPlatform.php" />
178205
</errorLevel>
@@ -189,6 +216,12 @@
189216
<file name="tests/Functional/Schema/SchemaManagerFunctionalTestCase.php"/>
190217
</errorLevel>
191218
</TypeDoesNotContainNull>
219+
<TypeDoesNotContainType>
220+
<errorLevel type="suppress">
221+
<!-- On PHP 7.4, pg_fetch_all() might return false for empty result sets. -->
222+
<file name="src/Driver/PgSQL/Result.php"/>
223+
</errorLevel>
224+
</TypeDoesNotContainType>
192225
<UndefinedDocblockClass>
193226
<errorLevel type="suppress">
194227
<!-- See https://github.com/vimeo/psalm/issues/5472 -->

src/Configuration.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Doctrine\DBAL;
66

77
use Doctrine\DBAL\Driver\Middleware;
8+
use Doctrine\DBAL\Schema\SchemaManagerFactory;
89
use Psr\Cache\CacheItemPoolInterface;
910

1011
/**
@@ -32,6 +33,8 @@ class Configuration
3233
*/
3334
protected bool $autoCommit = true;
3435

36+
private ?SchemaManagerFactory $schemaManagerFactory = null;
37+
3538
public function __construct()
3639
{
3740
$this->schemaAssetsFilter = static function (): bool {
@@ -116,4 +119,17 @@ public function getMiddlewares(): array
116119
{
117120
return $this->middlewares;
118121
}
122+
123+
public function getSchemaManagerFactory(): ?SchemaManagerFactory
124+
{
125+
return $this->schemaManagerFactory;
126+
}
127+
128+
/** @return $this */
129+
public function setSchemaManagerFactory(SchemaManagerFactory $schemaManagerFactory): self
130+
{
131+
$this->schemaManagerFactory = $schemaManagerFactory;
132+
133+
return $this;
134+
}
119135
}

0 commit comments

Comments
 (0)