Skip to content

Commit

Permalink
Realize column factory (#316)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tigrov authored Sep 1, 2024
1 parent fbbd88a commit 6e5994b
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 100 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Enh #312: Refactor `bit` type (@Tigrov)
- Enh #315: Refactor PHP type of `ColumnSchemaInterface` instances (@Tigrov)
- Enh #317: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov)
- Enh #316: Implement `ColumnFactory` class (@Tigrov)

## 1.2.0 March 21, 2024

Expand Down
82 changes: 82 additions & 0 deletions src/Column/ColumnFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Mssql\Column;

use Yiisoft\Db\Schema\Column\AbstractColumnFactory;
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
use Yiisoft\Db\Schema\SchemaInterface;

final class ColumnFactory extends AbstractColumnFactory
{
/**
* Mapping from physical column types (keys) to abstract column types (values).
*
* @var string[]
*/
private const TYPE_MAP = [
/** Exact numbers */
'bit' => SchemaInterface::TYPE_BOOLEAN,
'tinyint' => SchemaInterface::TYPE_TINYINT,
'smallint' => SchemaInterface::TYPE_SMALLINT,
'int' => SchemaInterface::TYPE_INTEGER,
'bigint' => SchemaInterface::TYPE_BIGINT,
'numeric' => SchemaInterface::TYPE_DECIMAL,
'decimal' => SchemaInterface::TYPE_DECIMAL,
'smallmoney' => SchemaInterface::TYPE_MONEY,
'money' => SchemaInterface::TYPE_MONEY,

/** Approximate numbers */
'float' => SchemaInterface::TYPE_FLOAT,
'real' => SchemaInterface::TYPE_FLOAT,
'double' => SchemaInterface::TYPE_DOUBLE,

/** Date and time */
'date' => SchemaInterface::TYPE_DATE,
'time' => SchemaInterface::TYPE_TIME,
'smalldatetime' => SchemaInterface::TYPE_DATETIME,
'datetime' => SchemaInterface::TYPE_DATETIME,
'datetime2' => SchemaInterface::TYPE_DATETIME,
'datetimeoffset' => SchemaInterface::TYPE_DATETIME,

/** Character strings */
'char' => SchemaInterface::TYPE_CHAR,
'varchar' => SchemaInterface::TYPE_STRING,
'text' => SchemaInterface::TYPE_TEXT,

/** Unicode character strings */
'nchar' => SchemaInterface::TYPE_CHAR,
'nvarchar' => SchemaInterface::TYPE_STRING,
'ntext' => SchemaInterface::TYPE_TEXT,

/** Binary strings */
'binary' => SchemaInterface::TYPE_BINARY,
'varbinary' => SchemaInterface::TYPE_BINARY,
'image' => SchemaInterface::TYPE_BINARY,

/**
* Other data types 'cursor' type can't be used with tables
*/
'timestamp' => SchemaInterface::TYPE_TIMESTAMP,
'hierarchyid' => SchemaInterface::TYPE_STRING,
'uniqueidentifier' => SchemaInterface::TYPE_STRING,
'sql_variant' => SchemaInterface::TYPE_STRING,
'xml' => SchemaInterface::TYPE_STRING,
'table' => SchemaInterface::TYPE_STRING,
];

protected function getType(string $dbType, array $info = []): string
{
return self::TYPE_MAP[$dbType] ?? SchemaInterface::TYPE_STRING;
}

public function fromType(string $type, array $info = []): ColumnSchemaInterface
{
if ($type === SchemaInterface::TYPE_BINARY) {
return (new BinaryColumnSchema($type))->load($info);
}

return parent::fromType($type, $info);
}
}
108 changes: 8 additions & 100 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,20 @@
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Helper\DbArrayHelper;
use Yiisoft\Db\Mssql\Column\BinaryColumnSchema;
use Yiisoft\Db\Mssql\Column\ColumnFactory;
use Yiisoft\Db\Schema\Builder\ColumnInterface;
use Yiisoft\Db\Schema\Column\ColumnFactoryInterface;
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
use Yiisoft\Db\Schema\TableSchemaInterface;

use function array_change_key_case;
use function array_map;
use function explode;
use function is_array;
use function md5;
use function preg_match;
use function serialize;
use function str_replace;
use function strcasecmp;
use function stripos;

/**
* Implements the MSSQL Server specific schema, supporting MSSQL Server 2017 and above.
Expand Down Expand Up @@ -68,67 +67,16 @@ final class Schema extends AbstractPdoSchema
*/
protected string|null $defaultSchema = 'dbo';

/**
* Mapping from physical column types (keys) to abstract column types (values).
*
* @var string[]
*/
private const TYPE_MAP = [
/** Exact numbers */
'bigint' => self::TYPE_BIGINT,
'numeric' => self::TYPE_DECIMAL,
'bit' => self::TYPE_BOOLEAN,
'smallint' => self::TYPE_SMALLINT,
'decimal' => self::TYPE_DECIMAL,
'smallmoney' => self::TYPE_MONEY,
'int' => self::TYPE_INTEGER,
'tinyint' => self::TYPE_TINYINT,
'money' => self::TYPE_MONEY,

/** Approximate numbers */
'float' => self::TYPE_FLOAT,
'double' => self::TYPE_DOUBLE,
'real' => self::TYPE_FLOAT,

/** Date and time */
'date' => self::TYPE_DATE,
'datetimeoffset' => self::TYPE_DATETIME,
'datetime2' => self::TYPE_DATETIME,
'smalldatetime' => self::TYPE_DATETIME,
'datetime' => self::TYPE_DATETIME,
'time' => self::TYPE_TIME,

/** Character strings */
'char' => self::TYPE_CHAR,
'varchar' => self::TYPE_STRING,
'text' => self::TYPE_TEXT,

/** Unicode character strings */
'nchar' => self::TYPE_CHAR,
'nvarchar' => self::TYPE_STRING,
'ntext' => self::TYPE_TEXT,

/** Binary strings */
'binary' => self::TYPE_BINARY,
'varbinary' => self::TYPE_BINARY,
'image' => self::TYPE_BINARY,

/**
* Other data types 'cursor' type can't be used with tables
*/
'timestamp' => self::TYPE_TIMESTAMP,
'hierarchyid' => self::TYPE_STRING,
'uniqueidentifier' => self::TYPE_STRING,
'sql_variant' => self::TYPE_STRING,
'xml' => self::TYPE_STRING,
'table' => self::TYPE_STRING,
];

public function createColumn(string $type, array|int|string|null $length = null): ColumnInterface
{
return new Column($type, $length);
}

public function getColumnFactory(): ColumnFactoryInterface
{
return new ColumnFactory();
}

/**
* Resolves the table name and schema name (if any).
*
Expand Down Expand Up @@ -419,14 +367,9 @@ protected function loadTableDefaultValues(string $tableName): array
private function loadColumnSchema(array $info): ColumnSchemaInterface
{
$dbType = $info['data_type'];
$type = $this->getColumnType($dbType, $info);
$isUnsigned = stripos($dbType, 'unsigned') !== false;
/** @psalm-var ColumnArray $info */
$column = $this->createColumnSchema($type, unsigned: $isUnsigned);
$column = $this->getColumnFactory()->fromDefinition($dbType);
$column->name($info['column_name']);
$column->size($info['size'] ?? null);
$column->precision($info['precision'] ?? null);
$column->scale($info['scale'] ?? null);
$column->allowNull($info['is_nullable'] === 'YES');
$column->dbType($dbType);
$column->enumValues([]); // MSSQL has only vague equivalents to enum.
Expand All @@ -439,41 +382,6 @@ private function loadColumnSchema(array $info): ColumnSchemaInterface
return $column;
}

/**
* Get the abstract data type for the database data type.
*
* @param string $dbType The database data type
* @param array $info Column information.
*
* @return string The abstract data type.
*/
private function getColumnType(string $dbType, array &$info): string
{
preg_match('/^(\w*)(?:\(([^)]+)\))?/', $dbType, $matches);
$dbType = strtolower($matches[1]);

if (!empty($matches[2])) {
$values = explode(',', $matches[2], 2);
$info['size'] = (int) $values[0];
$info['precision'] = (int) $values[0];

if (isset($values[1])) {
$info['scale'] = (int) $values[1];
}
}

return self::TYPE_MAP[$dbType] ?? self::TYPE_STRING;
}

protected function createColumnSchemaFromType(string $type, bool $isUnsigned = false): ColumnSchemaInterface
{
if ($type === self::TYPE_BINARY) {
return new BinaryColumnSchema($type);
}

return parent::createColumnSchemaFromType($type, $isUnsigned);
}

/**
* Converts column's default value according to {@see ColumnSchema::phpType} after retrieval from the database.
*
Expand Down
34 changes: 34 additions & 0 deletions tests/ColumnFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Mssql\Tests;

use Yiisoft\Db\Mssql\Tests\Support\TestTrait;
use Yiisoft\Db\Tests\AbstractColumnFactoryTest;

/**
* @group mssql
*/
final class ColumnFactoryTest extends AbstractColumnFactoryTest
{
use TestTrait;

/** @dataProvider \Yiisoft\Db\Mssql\Tests\Provider\ColumnFactoryProvider::dbTypes */
public function testFromDbType(string $dbType, string $expectedType, string $expectedInstanceOf): void
{
parent::testFromDbType($dbType, $expectedType, $expectedInstanceOf);
}

/** @dataProvider \Yiisoft\Db\Mssql\Tests\Provider\ColumnFactoryProvider::definitions */
public function testFromDefinition(string $definition, string $expectedType, string $expectedInstanceOf, array $expectedInfo = []): void
{
parent::testFromDefinition($definition, $expectedType, $expectedInstanceOf, $expectedInfo);
}

/** @dataProvider \Yiisoft\Db\Mssql\Tests\Provider\ColumnFactoryProvider::types */
public function testFromType(string $type, string $expectedType, string $expectedInstanceOf): void
{
parent::testFromType($type, $expectedType, $expectedInstanceOf);
}
}
54 changes: 54 additions & 0 deletions tests/Provider/ColumnFactoryProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Mssql\Tests\Provider;

use Yiisoft\Db\Mssql\Column\BinaryColumnSchema;
use Yiisoft\Db\Schema\Column\BooleanColumnSchema;
use Yiisoft\Db\Schema\Column\DoubleColumnSchema;
use Yiisoft\Db\Schema\Column\IntegerColumnSchema;
use Yiisoft\Db\Schema\Column\StringColumnSchema;

final class ColumnFactoryProvider extends \Yiisoft\Db\Tests\Provider\ColumnFactoryProvider
{
public static function dbTypes(): array
{
return [
// db type, expected abstract type, expected instance of
['bit', 'boolean', BooleanColumnSchema::class],
['tinyint', 'tinyint', IntegerColumnSchema::class],
['smallint', 'smallint', IntegerColumnSchema::class],
['int', 'integer', IntegerColumnSchema::class],
['bigint', 'bigint', IntegerColumnSchema::class],
['numeric', 'decimal', DoubleColumnSchema::class],
['decimal', 'decimal', DoubleColumnSchema::class],
['float', 'float', DoubleColumnSchema::class],
['real', 'float', DoubleColumnSchema::class],
['double', 'double', DoubleColumnSchema::class],
['smallmoney', 'money', StringColumnSchema::class],
['money', 'money', StringColumnSchema::class],
['date', 'date', StringColumnSchema::class],
['time', 'time', StringColumnSchema::class],
['smalldatetime', 'datetime', StringColumnSchema::class],
['datetime', 'datetime', StringColumnSchema::class],
['datetime2', 'datetime', StringColumnSchema::class],
['datetimeoffset', 'datetime', StringColumnSchema::class],
['char', 'char', StringColumnSchema::class],
['varchar', 'string', StringColumnSchema::class],
['text', 'text', StringColumnSchema::class],
['nchar', 'char', StringColumnSchema::class],
['nvarchar', 'string', StringColumnSchema::class],
['ntext', 'text', StringColumnSchema::class],
['binary', 'binary', BinaryColumnSchema::class],
['varbinary', 'binary', BinaryColumnSchema::class],
['image', 'binary', BinaryColumnSchema::class],
['timestamp', 'timestamp', StringColumnSchema::class],
['hierarchyid', 'string', StringColumnSchema::class],
['uniqueidentifier', 'string', StringColumnSchema::class],
['sql_variant', 'string', StringColumnSchema::class],
['xml', 'string', StringColumnSchema::class],
['table', 'string', StringColumnSchema::class],
];
}
}
9 changes: 9 additions & 0 deletions tests/SchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Mssql\Column\ColumnFactory;
use Yiisoft\Db\Mssql\Schema;
use Yiisoft\Db\Mssql\Tests\Support\TestTrait;
use Yiisoft\Db\Schema\SchemaInterface;
Expand Down Expand Up @@ -197,4 +198,12 @@ public function testNegativeDefaultValues(): void

$db->close();
}

public function testGetColumnFactory(): void
{
$db = $this->getConnection();
$factory = $db->getSchema()->getColumnFactory();

$this->assertInstanceOf(ColumnFactory::class, $factory);
}
}

0 comments on commit 6e5994b

Please sign in to comment.