diff --git a/UPGRADE.md b/UPGRADE.md
index 56fd9f39179..8a0d4a4c3c9 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -1,3 +1,11 @@
+Note about upgrading: Doctrine uses static and runtime mechanisms to raise
+awareness about deprecated code.
+
+- Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or
+ Static Analysis tools (like Psalm, phpstan)
+- Use of our low-overhead runtime deprecation API, details:
+ https://github.com/doctrine/deprecations/
+
# Upgrade to 4.0
## Removed `AbstractPlatform::getReservedKeywordsClass()`
@@ -301,11 +309,20 @@ Use `AbstractSchemaManager::listSchemaNames()` instead.
`PostgreSQLSchemaManager::getExistingSchemaSearchPaths()` and `::determineExistingSchemaSearchPaths()` have been marked internal.
+## `OracleSchemaManager` methods marked internal.
+
+`OracleSchemaManager::dropAutoincrement()` has been marked internal.
+
## Deprecated `AbstractPlatform::getReservedKeywordsClass()`
Instead of implementing `getReservedKeywordsClass()`, `AbstractPlatform` subclasses should implement
`createReservedKeywordsList()`.
+## Deprecated `ReservedWordsCommand::setKeywordListClass()`
+
+The usage of `ReservedWordsCommand::setKeywordListClass()` has been deprecated. To add or replace a keyword list,
+use `setKeywordList()` instead.
+
## Deprecated `$driverOptions` argument of `PDO\Statement::bindParam()` and `PDO\SQLSrv\Statement::bindParam()`
The usage of the `$driverOptions` argument of `PDO\Statement::bindParam()` and `PDO\SQLSrv\Statement::bindParam()` is deprecated.
@@ -321,6 +338,23 @@ Use `Connection::createSchemaManager()` instead.
The usage of `Connection::$_expr` and `Connection::getExpressionBuilder()` is deprecated.
Use `Connection::createExpressionBuilder()` instead.
+## Deprecated `QueryBuilder::execute()`
+
+The usage of `QueryBuilder::execute()` is deprecated. Use either `QueryBuilder::executeQuery()` or
+`QueryBuilder::executeStatement()`, depending on whether the queryBuilder is a query (SELECT) or a statement (INSERT,
+UPDATE, DELETE).
+
+You might also consider the use of the new shortcut methods, such as:
+
+- `fetchAllAssociative()`
+- `fetchAllAssociativeIndexed()`
+- `fetchAllKeyValue()`
+- `fetchAllNumeric()`
+- `fetchAssociative()`
+- `fetchFirstColumn()`
+- `fetchNumeric()`
+- `fetchOne()`
+
# Upgrade to 3.0
## BC BREAK: leading colon in named parameter names not supported
diff --git a/composer.json b/composer.json
index 28bca654a41..1dfae1bb9d7 100644
--- a/composer.json
+++ b/composer.json
@@ -45,6 +45,7 @@
"phpstan/phpstan-strict-rules": "0.12.2",
"phpunit/phpunit": "9.5.0",
"psalm/plugin-phpunit": "0.13.0",
+ "squizlabs/php_codesniffer": "3.6.0",
"symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
"vimeo/psalm": "4.6.4"
},
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index c8f0c9c3307..7b4cd3b5075 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -99,8 +99,7 @@
tests/Functional/DataAccess/FetchClass.php
-
-
+
- src/SQLParserUtils.php
src/Tools/Dumper.php
tests/Driver/StatementIteratorTest.php
tests/Tools/DumperTest.php
+
+
+ src/SQLParserUtils.php
+
+
src/Driver/SQLSrv/Result.php
diff --git a/src/Query/QueryBuilder.php b/src/Query/QueryBuilder.php
index 78c7d1766b3..7001e59a593 100644
--- a/src/Query/QueryBuilder.php
+++ b/src/Query/QueryBuilder.php
@@ -348,9 +348,35 @@ public function fetchFirstColumn(): array
return $this->connection->fetchFirstColumn($this->getSQL(), $this->params, $this->paramTypes);
}
+ /**
+ * Executes an SQL query (SELECT) and returns a Result.
+ *
+ * @throws Exception
+ */
+ public function executeQuery(): Result
+ {
+ return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes);
+ }
+
+ /**
+ * Executes an SQL statement and returns the number of affected rows.
+ *
+ * Should be used for INSERT, UPDATE and DELETE
+ *
+ * @return int The number of affected rows.
+ *
+ * @throws Exception
+ */
+ public function executeStatement(): int
+ {
+ return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes);
+ }
+
/**
* Executes this query using the bound parameters and their types.
*
+ * @deprecated Use {@link executeQuery()} or {@link executeStatement()} instead.
+ *
* @return Result|int
*
* @throws Exception
@@ -358,9 +384,21 @@ public function fetchFirstColumn(): array
public function execute()
{
if ($this->type === self::SELECT) {
+ Deprecation::trigger(
+ 'doctrine/dbal',
+ 'https://github.com/doctrine/dbal/pull/4578',
+ 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.'
+ );
+
return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes);
}
+ Deprecation::trigger(
+ 'doctrine/dbal',
+ 'https://github.com/doctrine/dbal/pull/4578',
+ 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.'
+ );
+
return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes);
}
diff --git a/src/Schema/OracleSchemaManager.php b/src/Schema/OracleSchemaManager.php
index 77142322d83..6c862c0004b 100644
--- a/src/Schema/OracleSchemaManager.php
+++ b/src/Schema/OracleSchemaManager.php
@@ -265,6 +265,8 @@ public function createDatabase(string $database): void
}
/**
+ * @internal The method should be only used from within the OracleSchemaManager class hierarchy.
+ *
* @throws Exception
*/
public function dropAutoincrement(string $table): bool
diff --git a/src/Tools/Console/Command/ReservedWordsCommand.php b/src/Tools/Console/Command/ReservedWordsCommand.php
index 44ac850f250..9f4591c8ead 100644
--- a/src/Tools/Console/Command/ReservedWordsCommand.php
+++ b/src/Tools/Console/Command/ReservedWordsCommand.php
@@ -19,6 +19,7 @@
use Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords;
use Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords;
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
+use Doctrine\Deprecations\Deprecation;
use InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -35,19 +36,8 @@
class ReservedWordsCommand extends Command
{
- /** @var array> */
- private $keywordListClasses = [
- 'db2' => DB2Keywords::class,
- 'mysql' => MySQLKeywords::class,
- 'mysql57' => MySQL57Keywords::class,
- 'mysql80' => MySQL80Keywords::class,
- 'mariadb102' => MariaDb102Keywords::class,
- 'oracle' => OracleKeywords::class,
- 'pgsql' => PostgreSQL94Keywords::class,
- 'pgsql100' => PostgreSQL100Keywords::class,
- 'sqlite' => SQLiteKeywords::class,
- 'sqlserver' => SQLServer2012Keywords::class,
- ];
+ /** @var array */
+ private $keywordLists;
/** @var ConnectionProvider */
private $connectionProvider;
@@ -56,6 +46,27 @@ public function __construct(ConnectionProvider $connectionProvider)
{
parent::__construct();
$this->connectionProvider = $connectionProvider;
+
+ $this->keywordLists = [
+ 'db2' => new DB2Keywords(),
+ 'mariadb102' => new MariaDb102Keywords(),
+ 'mysql' => new MySQLKeywords(),
+ 'mysql57' => new MySQL57Keywords(),
+ 'mysql80' => new MySQL80Keywords(),
+ 'oracle' => new OracleKeywords(),
+ 'pgsql' => new PostgreSQL94Keywords(),
+ 'pgsql100' => new PostgreSQL100Keywords(),
+ 'sqlite' => new SQLiteKeywords(),
+ 'sqlserver' => new SQLServer2012Keywords(),
+ ];
+ }
+
+ /**
+ * Add or replace a keyword list.
+ */
+ public function setKeywordList(string $name, KeywordList $keywordList): void
+ {
+ $this->keywordLists[$name] = $keywordList;
}
/**
@@ -65,7 +76,14 @@ public function __construct(ConnectionProvider $connectionProvider)
*/
public function setKeywordListClass(string $name, string $class): void
{
- $this->keywordListClasses[$name] = $class;
+ Deprecation::trigger(
+ 'doctrine/dbal',
+ 'https://github.com/doctrine/dbal/issues/4510',
+ 'ReservedWordsCommand::setKeywordListClass() is deprecated,'
+ . ' use ReservedWordsCommand::setKeywordList() instead.'
+ );
+
+ $this->keywordLists[$name] = new $class();
}
protected function configure(): void
@@ -86,8 +104,7 @@ protected function configure(): void
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 and Oracle
-keywords are checked:
+By default all supported platform keywords are checked:
%command.full_name%
@@ -98,17 +115,16 @@ protected function configure(): void
The following keyword lists are currently shipped with Doctrine:
+ * db2
+ * mariadb102
* mysql
* mysql57
* mysql80
- * mariadb102
+ * oracle
* pgsql
* pgsql100
* sqlite
- * oracle
* sqlserver
- * sqlserver2012
- * db2 (Not checked by default)
EOT
);
}
@@ -131,21 +147,20 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
if (count($keywordLists) === 0) {
- $keywordLists = array_keys($this->keywordListClasses);
+ $keywordLists = array_keys($this->keywordLists);
}
$keywords = [];
foreach ($keywordLists as $keywordList) {
- if (! isset($this->keywordListClasses[$keywordList])) {
+ if (! isset($this->keywordLists[$keywordList])) {
throw new InvalidArgumentException(sprintf(
'There exists no keyword list with name "%s". Known lists: %s',
$keywordList,
- implode(', ', array_keys($this->keywordListClasses))
+ implode(', ', array_keys($this->keywordLists))
));
}
- $class = $this->keywordListClasses[$keywordList];
- $keywords[] = new $class();
+ $keywords[] = $this->keywordLists[$keywordList];
}
$output->write(
diff --git a/tests/Platforms/SqlitePlatformTest.php b/tests/Platforms/SqlitePlatformTest.php
index 39298f880a6..7e08466a5c8 100644
--- a/tests/Platforms/SqlitePlatformTest.php
+++ b/tests/Platforms/SqlitePlatformTest.php
@@ -574,7 +574,7 @@ protected function getQuotedAlterTableChangeColumnLengthSQL(): array
public function testAlterTableRenameIndexInSchema(): void
{
self::markTestIncomplete(
- 'Test currently produces broken SQL due to SQLitePlatform::getAlterTable being broken ' .
+ 'Test currently produces broken SQL due to SQLitePlatform::getAlterTable() being broken ' .
'when used with schemas.'
);
}
@@ -582,7 +582,7 @@ public function testAlterTableRenameIndexInSchema(): void
public function testQuotesAlterTableRenameIndexInSchema(): void
{
self::markTestIncomplete(
- 'Test currently produces broken SQL due to SQLitePlatform::getAlterTable being broken ' .
+ 'Test currently produces broken SQL due to SQLitePlatform::getAlterTable() being broken ' .
'when used with schemas.'
);
}
diff --git a/tests/Query/QueryBuilderTest.php b/tests/Query/QueryBuilderTest.php
index 41be462992e..c14a8ef7fa0 100644
--- a/tests/Query/QueryBuilderTest.php
+++ b/tests/Query/QueryBuilderTest.php
@@ -9,6 +9,7 @@
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Query\QueryException;
+use Doctrine\DBAL\Result;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use PHPUnit\Framework\MockObject\MockObject;
@@ -1197,4 +1198,72 @@ public static function fetchProvider(): iterable
'SELECT id, username FROM user WHERE password = :password AND username != :username AND id != :id',
];
}
+
+ /**
+ * @param list|array $parameters
+ * @param array|array $parameterTypes
+ *
+ * @dataProvider fetchProvider
+ */
+ public function testExecuteQuery(
+ string $select,
+ string $from,
+ string $where,
+ array $parameters,
+ array $parameterTypes,
+ string $expectedSql
+ ): void {
+ $qb = new QueryBuilder($this->conn);
+ $mockedResult = $this->createMock(Result::class);
+
+ $this->conn->expects(self::once())
+ ->method('executeQuery')
+ ->with($expectedSql, $parameters, $parameterTypes)
+ ->willReturn($mockedResult);
+
+ $results = $qb->select($select)
+ ->from($from)
+ ->where($where)
+ ->setParameters($parameters, $parameterTypes)
+ ->executeQuery();
+
+ self::assertSame(
+ $mockedResult,
+ $results
+ );
+ }
+
+ public function testExecuteStatement(): void
+ {
+ $qb = new QueryBuilder($this->conn);
+ $mockedResult = 123;
+ $expectedSql = 'UPDATE users SET foo = ?, bar = ? WHERE bar = 1';
+
+ $parameters = [
+ 'foo' => 'jwage',
+ 'bar' => false,
+ ];
+
+ $parameterTypes = [
+ 'foo' => Types::STRING,
+ 'bar' => Types::BOOLEAN,
+ ];
+
+ $this->conn->expects(self::once())
+ ->method('executeStatement')
+ ->with($expectedSql, $parameters, $parameterTypes)
+ ->willReturn($mockedResult);
+
+ $results = $qb->update('users')
+ ->set('foo', '?')
+ ->set('bar', '?')
+ ->where('bar = 1')
+ ->setParameters($parameters, $parameterTypes)
+ ->executeStatement();
+
+ self::assertSame(
+ $mockedResult,
+ $results
+ );
+ }
}