diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000000..c2555ebbd46
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,4 @@
+Doctrine has [general contributing guidelines][contributor workflow], make
+sure you follow them.
+
+[contributor workflow]: https://www.doctrine-project.org/contribute/index.html
diff --git a/UPGRADE.md b/UPGRADE.md
index 9eb98d764a3..e8c8997dc67 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -1,5 +1,9 @@
# Upgrade to 2.11
+## Deprecated `Abstraction\Result`
+
+The usage of the `Doctrine\DBAL\Abstraction\Result` interface is deprecated. In DBAL 3.0, the statement result at the wrapper level will be represented by the `Doctrine\DBAL\Result` class.
+
## Deprecated the functionality of dropping client connections when dropping a database
The corresponding `getDisallowDatabaseConnectionsSQL()` and `getCloseActiveDatabaseConnectionsSQL` methods
diff --git a/docs/en/explanation/implicit-indexes.rst b/docs/en/explanation/implicit-indexes.rst
index f68edbe3ffb..9f96e8a34cb 100644
--- a/docs/en/explanation/implicit-indexes.rst
+++ b/docs/en/explanation/implicit-indexes.rst
@@ -6,13 +6,13 @@ with names such as ``IDX_885DBAFAA76ED395``? In this document, we will
distinguish three types of indexes:
user-defined indexes
- indexes you did ask for
+ indexes you did ask for
DBAL-defined indexes
- indexes you did not ask for, created on your behalf by the DBAL
+ indexes you did not ask for, created on your behalf by the DBAL
RDBMS-defined indexes
- indexes you did not ask for, created on your behalf by the RDBMS
+ indexes you did not ask for, created on your behalf by the RDBMS
RDBMS-defined indexes can be created by some database platforms when you
create a foreign key: they will create an index on the referencing
diff --git a/lib/Doctrine/DBAL/Abstraction/Result.php b/lib/Doctrine/DBAL/Abstraction/Result.php
index 42ff3419e37..2b1f8e69f3f 100644
--- a/lib/Doctrine/DBAL/Abstraction/Result.php
+++ b/lib/Doctrine/DBAL/Abstraction/Result.php
@@ -11,6 +11,8 @@
/**
* Abstraction-level result statement execution result. Provides additional methods on top
* of the driver-level interface.
+ *
+ * @deprecated
*/
interface Result extends DriverResult
{
diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php
index 82ef5d80170..af803e5db26 100644
--- a/lib/Doctrine/DBAL/Connection.php
+++ b/lib/Doctrine/DBAL/Connection.php
@@ -541,7 +541,7 @@ public function setFetchMode($fetchMode)
* Prepares and executes an SQL query and returns the first row of the result
* as an associative array.
*
- * @deprecated Use fetchAllAssociative()
+ * @deprecated Use fetchAssociative()
*
* @param string $sql The query SQL
* @param mixed[] $params The query parameters
@@ -560,7 +560,7 @@ public function fetchAssoc($sql, array $params = [], array $types = [])
* Prepares and executes an SQL query and returns the first row of the result
* as a numerically indexed array.
*
- * @deprecated Use fetchAllNumeric()
+ * @deprecated Use fetchNumeric()
*
* @param string $sql The query SQL
* @param mixed[] $params The query parameters
diff --git a/lib/Doctrine/DBAL/Driver/PDOConnection.php b/lib/Doctrine/DBAL/Driver/PDOConnection.php
index 43b8280ef70..8409952cb4e 100644
--- a/lib/Doctrine/DBAL/Driver/PDOConnection.php
+++ b/lib/Doctrine/DBAL/Driver/PDOConnection.php
@@ -5,6 +5,7 @@
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\PDO\Statement;
+use Doctrine\DBAL\ParameterType;
use PDO;
use PDOException;
use PDOStatement;
@@ -101,6 +102,14 @@ public function query()
}
}
+ /**
+ * {@inheritdoc}
+ */
+ public function quote($value, $type = ParameterType::STRING)
+ {
+ return parent::quote($value, $type);
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/lib/Doctrine/DBAL/Query/QueryBuilder.php b/lib/Doctrine/DBAL/Query/QueryBuilder.php
index 6c4aa6ef029..a1306754836 100644
--- a/lib/Doctrine/DBAL/Query/QueryBuilder.php
+++ b/lib/Doctrine/DBAL/Query/QueryBuilder.php
@@ -8,6 +8,7 @@
use Doctrine\DBAL\Query\Expression\CompositeExpression;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
+use function array_filter;
use function array_key_exists;
use function array_keys;
use function array_unshift;
@@ -829,6 +830,7 @@ public function where($predicates)
public function andWhere($where)
{
$args = func_get_args();
+ $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
$where = $this->getQueryPart('where');
if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
@@ -862,6 +864,7 @@ public function andWhere($where)
public function orWhere($where)
{
$args = func_get_args();
+ $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
$where = $this->getQueryPart('where');
if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
@@ -1010,6 +1013,7 @@ public function having($having)
public function andHaving($having)
{
$args = func_get_args();
+ $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
$having = $this->getQueryPart('having');
if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
@@ -1033,6 +1037,7 @@ public function andHaving($having)
public function orHaving($having)
{
$args = func_get_args();
+ $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
$having = $this->getQueryPart('having');
if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
diff --git a/lib/Doctrine/DBAL/Statement.php b/lib/Doctrine/DBAL/Statement.php
index 0e47e09303c..294866921ec 100644
--- a/lib/Doctrine/DBAL/Statement.php
+++ b/lib/Doctrine/DBAL/Statement.php
@@ -264,7 +264,15 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
- return $this->stmt->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
+ if ($ctorArgs !== null) {
+ return $this->stmt->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
+ }
+
+ if ($fetchArgument !== null) {
+ return $this->stmt->fetchAll($fetchMode, $fetchArgument);
+ }
+
+ return $this->stmt->fetchAll($fetchMode);
}
/**
diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php
index cc4069ee9d2..044b85e54aa 100644
--- a/lib/Doctrine/DBAL/Version.php
+++ b/lib/Doctrine/DBAL/Version.php
@@ -17,7 +17,7 @@ class Version
/**
* Current Doctrine Version.
*/
- public const VERSION = '2.11.0';
+ public const VERSION = '2.11.1';
/**
* Compares a Doctrine version with the current one.
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index c82ca439eca..8c6b82da6b0 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -110,6 +110,11 @@
lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php
+
+
+ lib/Doctrine/DBAL/Driver/PDOConnection.php
+
+
lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php
diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDO/ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDO/ConnectionTest.php
index f54cb07b06a..c9f748fbce6 100644
--- a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDO/ConnectionTest.php
+++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDO/ConnectionTest.php
@@ -1,5 +1,7 @@
driverConnection->query('foo');
}
+
+ /**
+ * This test ensures backward compatibility with DBAL 2.x and should be removed in 3.0.
+ */
+ public function testQuoteInteger(): void
+ {
+ self::assertSame("'1'", $this->connection->getWrappedConnection()->quote(1));
+ }
}
diff --git a/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php b/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php
index f5136c66c47..338d3a7aee4 100644
--- a/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php
+++ b/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php
@@ -29,6 +29,7 @@
use function version_compare;
use const PHP_OS;
+use const PHP_OS_FAMILY;
class ExceptionTest extends DbalFunctionalTestCase
{
@@ -303,7 +304,7 @@ public function testConnectionExceptionSqLite(): void
}
// mode 0 is considered read-only on Windows
- $mode = PHP_OS === 'Linux' ? 0444 : 0000;
+ $mode = PHP_OS_FAMILY === 'Windows' ? 0000 : 0444;
$filename = sprintf('%s/%s', sys_get_temp_dir(), 'doctrine_failed_connection_' . $mode . '.db');
diff --git a/tests/Doctrine/Tests/DBAL/Functional/ExternalPDOInstanceTest.php b/tests/Doctrine/Tests/DBAL/Functional/ExternalPDOInstanceTest.php
new file mode 100644
index 00000000000..42af9d29ea5
--- /dev/null
+++ b/tests/Doctrine/Tests/DBAL/Functional/ExternalPDOInstanceTest.php
@@ -0,0 +1,70 @@
+getDriver() instanceof PDOSqliteDriver) {
+ $this->markTestSkipped('External PDO instance tests are only run on PDO SQLite for now');
+ }
+
+ $pdo = new PDO('sqlite::memory:');
+
+ $this->connection = new Connection(['pdo' => $pdo], new PDOSqliteDriver());
+
+ $table = new Table('stmt_fetch_all');
+ $table->addColumn('a', 'integer');
+ $table->addColumn('b', 'integer');
+
+ $this->connection->getSchemaManager()->createTable($table);
+
+ $this->connection->insert('stmt_fetch_all', [
+ 'a' => 1,
+ 'b' => 2,
+ ]);
+ }
+
+ public function testFetchAllWithOneArgument(): void
+ {
+ $stmt = $this->connection->prepare('SELECT a, b FROM stmt_fetch_all');
+ $stmt->execute();
+
+ self::assertEquals([[1, 2]], $stmt->fetchAll(FetchMode::NUMERIC));
+ }
+
+ public function testFetchAllWithTwoArguments(): void
+ {
+ $stmt = $this->connection->prepare('SELECT a, b FROM stmt_fetch_all');
+ $stmt->execute();
+
+ self::assertEquals([2], $stmt->fetchAll(FetchMode::COLUMN, 1));
+ }
+
+ public function testFetchAllWithThreeArguments(): void
+ {
+ $stmt = $this->connection->prepare('SELECT a, b FROM stmt_fetch_all');
+ $stmt->execute();
+
+ [$obj] = $stmt->fetchAll(FetchMode::CUSTOM_OBJECT, StatementTestModel::class, ['foo', 'bar']);
+
+ $this->assertInstanceOf(StatementTestModel::class, $obj);
+
+ self::assertEquals(1, $obj->a);
+ self::assertEquals(2, $obj->b);
+ self::assertEquals('foo', $obj->x);
+ self::assertEquals('bar', $obj->y);
+ }
+}
diff --git a/tests/Doctrine/Tests/DBAL/Functional/StatementTestModel.php b/tests/Doctrine/Tests/DBAL/Functional/StatementTestModel.php
new file mode 100644
index 00000000000..16bb6e1e1a3
--- /dev/null
+++ b/tests/Doctrine/Tests/DBAL/Functional/StatementTestModel.php
@@ -0,0 +1,24 @@
+x = $x;
+ $this->y = $y;
+ }
+
+ /** @var int */
+ public $a;
+
+ /** @var int */
+ public $b;
+
+ /** @var string */
+ public $x;
+
+ /** @var string */
+ public $y;
+}
diff --git a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php
index bc671787877..038097c8910 100644
--- a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php
+++ b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php
@@ -949,4 +949,104 @@ public function testJoinWithNonUniqueAliasThrowsException(): void
$qb->getSQL();
}
+
+ public function testAndWhereEmptyStringStartingWithEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo');
+
+ $qb->andWhere('', 'a = b');
+
+ self::assertSame('SELECT id FROM foo WHERE a = b', $qb->getSQL());
+ }
+
+ public function testAndWhereEmptyStringStartingWithNonEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo')
+ ->where('a = b');
+
+ $qb->andWhere('', 'c = d');
+
+ self::assertSame('SELECT id FROM foo WHERE (a = b) AND (c = d)', $qb->getSQL());
+ }
+
+ public function testOrWhereEmptyStringStartingWithEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo');
+
+ $qb->orWhere('', 'a = b');
+
+ self::assertSame('SELECT id FROM foo WHERE a = b', $qb->getSQL());
+ }
+
+ public function testOrWhereEmptyStringStartingWithNonEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo')
+ ->where('a = b');
+
+ $qb->orWhere('', 'c = d');
+
+ self::assertSame('SELECT id FROM foo WHERE (a = b) OR (c = d)', $qb->getSQL());
+ }
+
+ public function testAndHavingEmptyStringStartingWithEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo');
+
+ $qb->andHaving('', 'a = b');
+
+ self::assertSame('SELECT id FROM foo HAVING a = b', $qb->getSQL());
+ }
+
+ public function testAndHavingEmptyStringStartingWithNonEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo')
+ ->having('a = b');
+
+ $qb->andHaving('', 'c = d');
+
+ self::assertSame('SELECT id FROM foo HAVING (a = b) AND (c = d)', $qb->getSQL());
+ }
+
+ public function testOrHavingEmptyStringStartingWithEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo');
+
+ $qb->orHaving('', 'a = b');
+
+ self::assertSame('SELECT id FROM foo HAVING a = b', $qb->getSQL());
+ }
+
+ public function testOrHavingEmptyStringStartingWithNonEmptyExpression(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+
+ $qb->select('id')
+ ->from('foo')
+ ->having('a = b');
+
+ $qb->orHaving('', 'c = d');
+
+ self::assertSame('SELECT id FROM foo HAVING (a = b) OR (c = d)', $qb->getSQL());
+ }
}