Skip to content

Commit

Permalink
Merge branch '3.1.x' into 4.0.x
Browse files Browse the repository at this point in the history
  • Loading branch information
morozov committed Apr 9, 2021
2 parents 461b3b7 + 8bfd0ba commit 2a2943c
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 30 deletions.
34 changes: 34 additions & 0 deletions 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()`
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -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"
},
Expand Down
9 changes: 6 additions & 3 deletions phpcs.xml.dist
Expand Up @@ -99,8 +99,7 @@
<exclude-pattern>tests/Functional/DataAccess/FetchClass.php</exclude-pattern>
</rule>

<!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps">
<rule ref="Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps">
<!--
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:
Expand All @@ -109,12 +108,16 @@
<exclude-pattern>src/Driver/IBMDB2/Connection.php</exclude-pattern>
<exclude-pattern>src/Driver/Mysqli/Exception/ConnectionFailed.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>
<exclude-pattern>tests/Driver/StatementIteratorTest.php</exclude-pattern>
<exclude-pattern>tests/Tools/DumperTest.php</exclude-pattern>
</rule>

<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps">
<exclude-pattern>src/SQLParserUtils.php</exclude-pattern>
</rule>

<!-- some statement classes close cursor using an empty while-loop -->
<rule ref="Generic.CodeAnalysis.EmptyStatement.DetectedWhile">
<exclude-pattern>src/Driver/SQLSrv/Result.php</exclude-pattern>
Expand Down
38 changes: 38 additions & 0 deletions src/Query/QueryBuilder.php
Expand Up @@ -348,19 +348,57 @@ 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
*/
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);
}

Expand Down
2 changes: 2 additions & 0 deletions src/Schema/OracleSchemaManager.php
Expand Up @@ -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
Expand Down
65 changes: 40 additions & 25 deletions src/Tools/Console/Command/ReservedWordsCommand.php
Expand Up @@ -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;
Expand All @@ -35,19 +36,8 @@

class ReservedWordsCommand extends Command
{
/** @var array<string,class-string<KeywordList>> */
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<string,KeywordList> */
private $keywordLists;

/** @var ConnectionProvider */
private $connectionProvider;
Expand All @@ -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;
}

/**
Expand All @@ -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
Expand All @@ -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:
<info>%command.full_name%</info>
Expand All @@ -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
);
}
Expand All @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions tests/Platforms/SqlitePlatformTest.php
Expand Up @@ -574,15 +574,15 @@ 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.'
);
}

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.'
);
}
Expand Down
69 changes: 69 additions & 0 deletions tests/Query/QueryBuilderTest.php
Expand Up @@ -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;
Expand Down Expand Up @@ -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<mixed>|array<string, mixed> $parameters
* @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $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
);
}
}

0 comments on commit 2a2943c

Please sign in to comment.