-
-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into refactor-batchInsert
- Loading branch information
Showing
30 changed files
with
1,147 additions
and
198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,37 @@ | ||
# Upgrading Instructions for Yii Database | ||
|
||
This file contains the upgrade notes for the Yii Database. | ||
These notes highlight changes that could break your application when you upgrade it from one version to another. | ||
Even though we try to ensure backwards compatibility (BC) as much as possible, sometimes | ||
it isn't possible or very complicated to avoid it and still create a good solution to | ||
a problem. While upgrade to Yii 3.0 might require substantial changes to both your application and extensions, | ||
the changes are bearable and require "refactoring", not "rewrite". | ||
All the "Yes, it is" cool stuff, and Yii soul is still in place. | ||
|
||
Changes summary: | ||
|
||
* `Yiisoft\Db\Connection::$charset` has been removed. All supported PDO classes allow you to specify the connection | ||
charset in the DSN. | ||
The following upgrading instructions are cumulative. That is, if you want to upgrade from version A to version C and | ||
there is version B between A and C, you need to following the instructions for both A and B. | ||
|
||
## Upgrade from 1.x to 2.x | ||
|
||
### `ColumnInterface` as column type | ||
|
||
Add `ColumnInterface` support and change type of parameter `$type` from `string` to `ColumnInterface|string` | ||
in `addColumn()` method of your classes that implement the following interfaces: | ||
|
||
- `Yiisoft\Db\Command\CommandInterface`; | ||
- `Yiisoft\Db\QueryBuilder\DDLQueryBuilderInterface`; | ||
|
||
… or inherit from the following classes: | ||
|
||
- `Yiisoft\Db\Command\AbstractCommand`; | ||
- `Yiisoft\Db\QueryBuilder\AbstractDDLQueryBuilder`; | ||
- `Yiisoft\Db\QueryBuilder\AbstractQueryBuilder`. | ||
|
||
### Scalar values for columns in `Query` | ||
|
||
Change `$columns` parameter type from `array|string|ExpressionInterface` to `array|bool|float|int|string|ExpressionInterface` | ||
in methods `select()` and `addSelect()` of your classes that implement `Yiisoft\Db\Query\QueryPartsInterface`. | ||
|
||
Add support any scalar values for `$columns` parameter of these methods in your classes that implement | ||
`Yiisoft\Db\Query\QueryPartsInterface` or inherit `Yiisoft\Db\Query\Query`. | ||
|
||
### Build `Expression` instances inside `Expression::$params` | ||
|
||
`ExpressionBuilder` is replaced by an abstract class `AbstractExpressionBuilder` with an instance of the | ||
`QueryBuilderInterface` parameter in the constructor. Each DBMS driver should implement its own expression builder. | ||
|
||
`Expression::$params` can contain: | ||
- non-unique placeholder names, they will be replaced with unique names. | ||
- `Expression` instances, they will be built when building a query using `QueryBuilder`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Yiisoft\Db\Expression; | ||
|
||
use Yiisoft\Db\Command\Param; | ||
use Yiisoft\Db\Connection\ConnectionInterface; | ||
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface; | ||
use Yiisoft\Db\Syntax\AbstractSqlParser; | ||
|
||
use function array_merge; | ||
use function count; | ||
use function strlen; | ||
use function substr; | ||
use function substr_replace; | ||
|
||
/** | ||
* It's used to build expressions for use in database queries. | ||
* | ||
* It provides a {@see build()} method for creating various types of expressions, such as conditions, joins, and | ||
* ordering clauses. | ||
* | ||
* These expressions can be used with the query builder to build complex and customizable database queries | ||
* {@see Expression} class. | ||
* | ||
* @psalm-import-type ParamsType from ConnectionInterface | ||
*/ | ||
abstract class AbstractExpressionBuilder implements ExpressionBuilderInterface | ||
{ | ||
public function __construct(private QueryBuilderInterface $queryBuilder) | ||
{ | ||
} | ||
|
||
/** | ||
* Builds an SQL expression from the given expression object. | ||
* | ||
* This method is called by the query builder to build SQL expressions from {@see ExpressionInterface} objects. | ||
* | ||
* @param Expression $expression The expression to build. | ||
* @param array $params The parameters to be bound to the query. | ||
* | ||
* @psalm-param ParamsType $params | ||
* | ||
* @return string SQL expression. | ||
*/ | ||
public function build(ExpressionInterface $expression, array &$params = []): string | ||
{ | ||
$sql = $expression->__toString(); | ||
$expressionParams = $expression->getParams(); | ||
|
||
if (empty($expressionParams)) { | ||
return $sql; | ||
} | ||
|
||
if (isset($expressionParams[0])) { | ||
$params = array_merge($params, $expressionParams); | ||
return $sql; | ||
} | ||
|
||
$nonUniqueReplacements = $this->appendParams($expressionParams, $params); | ||
$expressionReplacements = $this->buildParamExpressions($expressionParams, $params); | ||
|
||
$replacements = $this->mergeReplacements($nonUniqueReplacements, $expressionReplacements); | ||
|
||
if (empty($replacements)) { | ||
return $sql; | ||
} | ||
|
||
return $this->replacePlaceholders($sql, $replacements); | ||
} | ||
|
||
/** | ||
* Appends parameters to the list of query parameters replacing non-unique parameters with unique ones. | ||
* | ||
* @param array $expressionParams Parameters to be appended. | ||
* @param array $params Parameters to be bound to the query. | ||
* | ||
* @psalm-param ParamsType $expressionParams | ||
* @psalm-param ParamsType $params | ||
* | ||
* @return string[] Replacements for non-unique parameters. | ||
*/ | ||
private function appendParams(array &$expressionParams, array &$params): array | ||
{ | ||
$nonUniqueParams = []; | ||
|
||
/** @var non-empty-string $name */ | ||
foreach ($expressionParams as $name => $value) { | ||
$paramName = $name[0] === ':' ? substr($name, 1) : $name; | ||
|
||
if (!isset($params[$paramName]) && !isset($params[":$paramName"])) { | ||
$params[$name] = $value; | ||
continue; | ||
} | ||
|
||
$nonUniqueParams[$name] = $value; | ||
} | ||
|
||
$replacements = []; | ||
|
||
/** @var non-empty-string $name */ | ||
foreach ($nonUniqueParams as $name => $value) { | ||
$paramName = $name[0] === ':' ? substr($name, 1) : $name; | ||
$uniqueName = $this->getUniqueName($paramName, $params); | ||
|
||
$replacements[":$paramName"] = ":$uniqueName"; | ||
|
||
if ($name[0] === ':') { | ||
$uniqueName = ":$uniqueName"; | ||
} | ||
|
||
$params[$uniqueName] = $value; | ||
$expressionParams[$uniqueName] = $value; | ||
unset($expressionParams[$name]); | ||
} | ||
|
||
return $replacements; | ||
} | ||
|
||
/** | ||
* Build expression values of parameters. | ||
* | ||
* @param array $expressionParams Parameters from the expression. | ||
* @param array $params Parameters to be bound to the query. | ||
* | ||
* @psalm-param ParamsType $expressionParams | ||
* @psalm-param ParamsType $params | ||
* | ||
* @return string[] Replacements for parameters. | ||
*/ | ||
private function buildParamExpressions(array $expressionParams, array &$params): array | ||
{ | ||
$replacements = []; | ||
|
||
/** @var non-empty-string $name */ | ||
foreach ($expressionParams as $name => $value) { | ||
if (!$value instanceof ExpressionInterface || $value instanceof Param) { | ||
continue; | ||
} | ||
|
||
$placeholder = $name[0] !== ':' ? ":$name" : $name; | ||
$replacements[$placeholder] = $this->queryBuilder->buildExpression($value, $params); | ||
|
||
/** @psalm-var ParamsType $params */ | ||
unset($params[$name]); | ||
} | ||
|
||
return $replacements; | ||
} | ||
|
||
/** | ||
* Merges replacements for non-unique parameters with replacements for expression parameters. | ||
* | ||
* @param string[] $replacements Replacements for non-unique parameters. | ||
* @param string[] $expressionReplacements Replacements for expression parameters. | ||
* | ||
* @return string[] Merged replacements. | ||
*/ | ||
private function mergeReplacements(array $replacements, array $expressionReplacements): array | ||
{ | ||
if (empty($replacements)) { | ||
return $expressionReplacements; | ||
} | ||
|
||
if (empty($expressionReplacements)) { | ||
return $replacements; | ||
} | ||
|
||
/** @var non-empty-string $value */ | ||
foreach ($replacements as $name => $value) { | ||
if (isset($expressionReplacements[$value])) { | ||
$replacements[$name] = $expressionReplacements[$value]; | ||
unset($expressionReplacements[$value]); | ||
} | ||
} | ||
|
||
return $replacements + $expressionReplacements; | ||
} | ||
|
||
/** | ||
* Returns a unique name for the parameter without colon at the beginning. | ||
* | ||
* @param string $name Name of the parameter without colon at the beginning. | ||
* @param array $params Parameters to be bound to the query. | ||
* | ||
* @psalm-param ParamsType $params | ||
* | ||
* @return string Unique name of the parameter with colon at the beginning. | ||
* | ||
* @psalm-return non-empty-string | ||
*/ | ||
private function getUniqueName(string $name, array $params): string | ||
{ | ||
$uniqueName = $name . '_0'; | ||
|
||
for ($i = 1; isset($params[$uniqueName]) || isset($params[":$uniqueName"]); ++$i) { | ||
$uniqueName = $name . '_' . $i; | ||
} | ||
|
||
return $uniqueName; | ||
} | ||
|
||
/** | ||
* Replaces placeholders with replacements in a SQL expression. | ||
* | ||
* @param string $sql SQL expression where the placeholder should be replaced. | ||
* @param string[] $replacements Replacements for placeholders. | ||
* | ||
* @return string SQL expression with replaced placeholders. | ||
*/ | ||
private function replacePlaceholders(string $sql, array $replacements): string | ||
{ | ||
$parser = $this->createSqlParser($sql); | ||
$offset = 0; | ||
|
||
while (null !== $placeholder = $parser->getNextPlaceholder($position)) { | ||
if (isset($replacements[$placeholder])) { | ||
/** @var int $position */ | ||
$sql = substr_replace($sql, $replacements[$placeholder], $position + $offset, strlen($placeholder)); | ||
|
||
if (count($replacements) === 1) { | ||
break; | ||
} | ||
|
||
$offset += strlen($replacements[$placeholder]) - strlen($placeholder); | ||
unset($replacements[$placeholder]); | ||
} | ||
} | ||
|
||
return $sql; | ||
} | ||
|
||
/** | ||
* Creates an instance of {@see AbstractSqlParser} for the given SQL expression. | ||
* | ||
* @param string $sql SQL expression to be parsed. | ||
* | ||
* @return AbstractSqlParser SQL parser instance. | ||
*/ | ||
abstract protected function createSqlParser(string $sql): AbstractSqlParser; | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.