Skip to content

Commit

Permalink
Merge branch 'master' into docker
Browse files Browse the repository at this point in the history
  • Loading branch information
arogachev committed Nov 21, 2024
2 parents d466d6f + ba043b6 commit 86c0fb9
Show file tree
Hide file tree
Showing 25 changed files with 422 additions and 167 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,18 @@
- Enh #865: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov, @vjik)
- Enh #798: Allow `QueryInterface::one()` and `QueryInterface::all()` to return objects (@darkdef, @Tigrov)
- Enh #872: Use `#[\SensitiveParameter]` attribute to mark sensitive parameters (@heap-s)
- New #864: Realize column factory (@Tigrov)
- New #864, #897, #898: Realize column factory (@Tigrov)
- Enh #875: Ignore "Packets out of order..." warnings in `AbstractPdoCommand::internalExecute()` method (@Tigrov)
- Enh #877: Separate column type constants (@Tigrov)
- Enh #878: Realize `ColumnBuilder` class (@Tigrov)
- New #878: Realize `ColumnBuilder` class (@Tigrov)
- New #878, #900: Realize `ColumnDefinitionParser` class (@Tigrov)
- Enh #881: Refactor `ColumnSchemaInterface` and `AbstractColumnSchema` (@Tigrov)
- New #882: Move `ArrayColumnSchema` and `StructuredColumnSchema` classes from `db-pgsql` package (@Tigrov)
- New #883: Add `ColumnDefinitionBuilder` class and `QueryBuilderInterface::buildColumnDefinition()` method (@Tigrov)
- New #883, #901: Add `ColumnDefinitionBuilder` class and `QueryBuilderInterface::buildColumnDefinition()` method (@Tigrov)
- Enh #885: Refactor `AbstractDsn` class (@Tigrov)
- Chg #889: Update `AbstractDMLQueryBuilder::insertBatch()` method (@Tigrov)
- Enh #890: Add properties of `AbstractColumnSchema` class to constructor (@Tigrov)
- New #899: Add `ColumnSchemaInterface::hasDefaultValue()` and `ColumnSchemaInterface::null()` methods (@Tigrov)

## 1.3.0 March 21, 2024

Expand Down
2 changes: 2 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ and the following changes were made:
- `reference(ForeignKeyConstraint|null $reference)` method is added;
- `getReference()` method is added;
- `notNull(bool $notNull = true)` method is added;
- `null()` method is added;
- `isNotNull()` method is added;
- `unique(bool $unique = true)` method is added;
- `isUnique()` method is added;
- `hasDefaultValue()` method is added;
- all `AbstractColumnSchema` class properties except `$type` moved to constructor;
- added `DEFAULT_TYPE` constant to `AbstractColumnSchema` class;
- added method chaining.
Expand Down
28 changes: 13 additions & 15 deletions src/QueryBuilder/AbstractColumnDefinitionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ protected function buildDefault(ColumnSchemaInterface $column): string
return ' DEFAULT ' . static::GENERATE_UUID_EXPRESSION;
}

if ($column->isAutoIncrement() && $column->getType() !== ColumnType::UUID
|| $column->getDefaultValue() === null
) {
if ($column->isAutoIncrement() && $column->getType() !== ColumnType::UUID) {
return '';
}

Expand All @@ -147,21 +145,21 @@ protected function buildDefault(ColumnSchemaInterface $column): string
*/
protected function buildDefaultValue(ColumnSchemaInterface $column): string|null
{
$value = $column->dbTypecast($column->getDefaultValue());

if ($value === null) {
if (!$column->hasDefaultValue()) {
return null;
}

if ($value instanceof ExpressionInterface) {
return $this->queryBuilder->buildExpression($value);
}
$value = $column->dbTypecast($column->getDefaultValue());

/** @var string */
return match (gettype($value)) {
GettypeResult::INTEGER => (string) $value,
GettypeResult::DOUBLE => (string) $value,
GettypeResult::BOOLEAN => $value ? 'TRUE' : 'FALSE',
GettypeResult::NULL => $column->isNotNull() !== true ? 'NULL' : null,
GettypeResult::OBJECT => $value instanceof ExpressionInterface
? $this->queryBuilder->buildExpression($value)
: $this->queryBuilder->quoter()->quoteValue((string) $value),
default => $this->queryBuilder->quoter()->quoteValue((string) $value),
};
}
Expand All @@ -186,7 +184,11 @@ protected function buildExtra(ColumnSchemaInterface $column): string
*/
protected function buildNotNull(ColumnSchemaInterface $column): string
{
return $column->isNotNull() ? ' NOT NULL' : '';
return match ($column->isNotNull()) {
true => ' NOT NULL',
false => ' NULL',
default => '',
};
}

/**
Expand Down Expand Up @@ -274,11 +276,7 @@ protected function buildOnUpdate(string $onUpdate): string
*/
protected function buildType(ColumnSchemaInterface $column): string
{
$dbType = $column->getDbType();

if ($dbType === null) {
$dbType = $this->getDbType($column);
}
$dbType = $this->getDbType($column);

if (empty($dbType)
|| $dbType[-1] === ')'
Expand Down
202 changes: 149 additions & 53 deletions src/Schema/Column/AbstractColumnFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@

use Yiisoft\Db\Constant\ColumnType;
use Yiisoft\Db\Constant\PseudoType;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Syntax\ColumnDefinitionParser;

use function array_diff_key;
use function is_numeric;
use function preg_match;
use function str_replace;
use function substr;

use const PHP_INT_SIZE;

/**
Expand All @@ -18,29 +25,18 @@
abstract class AbstractColumnFactory implements ColumnFactoryInterface
{
/**
* Get the abstract database type for a database column type.
*
* @param string $dbType The database column type.
* @param array $info The column information.
* The mapping from physical column types (keys) to abstract column types (values).
*
* @return string The abstract database type.
* @var string[]
*
* @psalm-param ColumnInfo $info
* @psalm-return ColumnType::*
*/
abstract protected function getType(string $dbType, array $info = []): string;

/**
* Checks if the column type is a database type.
* @psalm-var array<string, ColumnType::*>
*/
abstract protected function isDbType(string $dbType): bool;
protected const TYPE_MAP = [];

public function fromDbType(string $dbType, array $info = []): ColumnSchemaInterface
{
unset($info['dbType']);
$type = $info['type'] ?? $this->getType($dbType, $info);
unset($info['type']);
$info['dbType'] = $dbType;
$type = $info['type'] ?? $this->getType($dbType, $info);

return $this->fromType($type, $info);
}
Expand All @@ -60,77 +56,124 @@ public function fromDefinition(string $definition, array $info = []): ColumnSche
$info += $definitionInfo;

if ($this->isDbType($type)) {
unset($info['dbType']);
return $this->fromDbType($type, $info);
}

if ($this->isType($type)) {
unset($info['type']);
return $this->fromType($type, $info);
}

if ($this->isPseudoType($type)) {
return $this->fromPseudoType($type, $info);
}

unset($info['dbType']);
return $this->fromDbType($type, $info);
}

/**
* @psalm-suppress MixedArgument
* @psalm-suppress InvalidArgument
* @psalm-suppress InvalidNamedArgument
*/
public function fromPseudoType(string $pseudoType, array $info = []): ColumnSchemaInterface
{
$info['primaryKey'] = true;
$info['autoIncrement'] = true;

return match ($pseudoType) {
PseudoType::PK => new IntegerColumnSchema(ColumnType::INTEGER, ...$info),
PseudoType::UPK => new IntegerColumnSchema(ColumnType::INTEGER, ...[...$info, 'unsigned' => true]),
PseudoType::BIGPK => PHP_INT_SIZE !== 8
? new BigIntColumnSchema(ColumnType::BIGINT, ...$info)
: new IntegerColumnSchema(ColumnType::BIGINT, ...$info),
PseudoType::UBIGPK => new BigIntColumnSchema(ColumnType::BIGINT, ...[...$info, 'unsigned' => true]),
PseudoType::UUID_PK => new StringColumnSchema(ColumnType::UUID, ...$info),
PseudoType::UUID_PK_SEQ => new StringColumnSchema(ColumnType::UUID, ...$info),
if ($pseudoType === PseudoType::UPK || $pseudoType === PseudoType::UBIGPK) {
$info['unsigned'] = true;
}

$type = match ($pseudoType) {
PseudoType::PK => ColumnType::INTEGER,
PseudoType::UPK => ColumnType::INTEGER,
PseudoType::BIGPK => ColumnType::BIGINT,
PseudoType::UBIGPK => ColumnType::BIGINT,
PseudoType::UUID_PK => ColumnType::UUID,
PseudoType::UUID_PK_SEQ => ColumnType::UUID,
};

return $this->fromType($type, $info);
}

public function fromType(string $type, array $info = []): ColumnSchemaInterface
{
unset($info['type']);

if ($type === ColumnType::ARRAY && empty($info['column']) && !empty($info['dbType'])) {
/** @psalm-suppress ArgumentTypeCoercion */
$info['column'] = $this->fromDbType(
$info['dbType'],
array_diff_key($info, ['dimension' => 1, 'defaultValueRaw' => 1])
);
}

$columnClass = $this->getColumnClass($type, $info);

$column = new $columnClass($type, ...$info);

if (isset($info['defaultValueRaw'])) {
$column->defaultValue($this->normalizeDefaultValue($info['defaultValueRaw'], $column));
}

return $column;
}

/**
* @psalm-suppress InvalidNamedArgument
* Returns the column definition parser.
*/
public function fromType(string $type, array $info = []): ColumnSchemaInterface
protected function columnDefinitionParser(): ColumnDefinitionParser
{
return new ColumnDefinitionParser();
}

/**
* @psalm-param ColumnType::* $type
* @param ColumnInfo $info
*
* @psalm-return class-string<ColumnSchemaInterface>
*/
protected function getColumnClass(string $type, array $info = []): string
{
return match ($type) {
ColumnType::BOOLEAN => new BooleanColumnSchema($type, ...$info),
ColumnType::BIT => new BitColumnSchema($type, ...$info),
ColumnType::TINYINT => new IntegerColumnSchema($type, ...$info),
ColumnType::SMALLINT => new IntegerColumnSchema($type, ...$info),
ColumnType::BOOLEAN => BooleanColumnSchema::class,
ColumnType::BIT => BitColumnSchema::class,
ColumnType::TINYINT => IntegerColumnSchema::class,
ColumnType::SMALLINT => IntegerColumnSchema::class,
ColumnType::INTEGER => PHP_INT_SIZE !== 8 && !empty($info['unsigned'])
? new BigIntColumnSchema($type, ...$info)
: new IntegerColumnSchema($type, ...$info),
? BigIntColumnSchema::class
: IntegerColumnSchema::class,
ColumnType::BIGINT => PHP_INT_SIZE !== 8 || !empty($info['unsigned'])
? new BigIntColumnSchema($type, ...$info)
: new IntegerColumnSchema($type, ...$info),
ColumnType::DECIMAL => new DoubleColumnSchema($type, ...$info),
ColumnType::FLOAT => new DoubleColumnSchema($type, ...$info),
ColumnType::DOUBLE => new DoubleColumnSchema($type, ...$info),
ColumnType::BINARY => new BinaryColumnSchema($type, ...$info),
ColumnType::STRUCTURED => new StructuredColumnSchema($type, ...$info),
ColumnType::JSON => new JsonColumnSchema($type, ...$info),
default => new StringColumnSchema($type, ...$info),
? BigIntColumnSchema::class
: IntegerColumnSchema::class,
ColumnType::DECIMAL => DoubleColumnSchema::class,
ColumnType::FLOAT => DoubleColumnSchema::class,
ColumnType::DOUBLE => DoubleColumnSchema::class,
ColumnType::BINARY => BinaryColumnSchema::class,
ColumnType::ARRAY => ArrayColumnSchema::class,
ColumnType::STRUCTURED => StructuredColumnSchema::class,
ColumnType::JSON => JsonColumnSchema::class,
default => StringColumnSchema::class,
};
}

/**
* Returns the column definition parser.
* Get the abstract database type for a database column type.
*
* @param string $dbType The database column type.
* @param array $info The column information.
*
* @return string The abstract database type.
*
* @psalm-param ColumnInfo $info
* @psalm-return ColumnType::*
*/
protected function columnDefinitionParser(): ColumnDefinitionParser
protected function getType(string $dbType, array $info = []): string
{
return new ColumnDefinitionParser();
return static::TYPE_MAP[$dbType] ?? ColumnType::STRING;
}

/**
* Checks if the column type is a database type.
*/
protected function isDbType(string $dbType): bool
{
return isset(static::TYPE_MAP[$dbType]);
}

/**
Expand Down Expand Up @@ -184,4 +227,57 @@ protected function isType(string $type): bool
default => false,
};
}

/**
* Converts column's default value according to {@see ColumnSchemaInterface::getPhpType()} after retrieval from the
* database.
*
* @param string|null $defaultValue The default value retrieved from the database.
* @param ColumnSchemaInterface $column The column schema object.
*
* @return mixed The normalized default value.
*/
protected function normalizeDefaultValue(string|null $defaultValue, ColumnSchemaInterface $column): mixed
{
if (
$defaultValue === null
|| $defaultValue === ''
|| $column->isPrimaryKey()
|| $column->isComputed()
|| preg_match('/^\(?NULL\b/i', $defaultValue) === 1
) {
return null;
}

return $this->normalizeNotNullDefaultValue($defaultValue, $column);
}

/**
* Converts a not null default value according to {@see ColumnSchemaInterface::getPhpType()}.
*/
protected function normalizeNotNullDefaultValue(string $defaultValue, ColumnSchemaInterface $column): mixed
{
$value = $defaultValue;

if ($value[0] === '(' && $value[-1] === ')') {
$value = substr($value, 1, -1);
}

if (is_numeric($value)) {
return $column->phpTypecast($value);
}

if ($value[0] === "'" && $value[-1] === "'") {
$value = substr($value, 1, -1);
$value = str_replace("''", "'", $value);

return $column->phpTypecast($value);
}

return match ($value) {
'true' => true,
'false' => false,
default => new Expression($defaultValue),
};
}
}
Loading

0 comments on commit 86c0fb9

Please sign in to comment.