Skip to content

Commit

Permalink
Realize column factory (#276)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tigrov authored Sep 1, 2024
1 parent b15e5e5 commit 40bb07d
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 81 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Chg #272: Replace call of `SchemaInterface::getRawTableName()` to `QuoterInterface::getRawTableName()` (@Tigrov)
- Enh #275: Refactor PHP type of `ColumnSchemaInterface` instances (@Tigrov)
- Enh #277: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov)
- Enh #276: Implement `ColumnFactory` class (@Tigrov)

## 1.3.0 March 21, 2024

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

declare(strict_types=1);

namespace Yiisoft\Db\Oracle\Column;

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

use function preg_replace;
use function strtolower;

final class ColumnFactory extends AbstractColumnFactory
{
/**
* The mapping from physical column types (keys) to abstract column types (values).
*
* @link https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html
*
* @var string[]
*
* @psalm-suppress MissingClassConstType
*/
private const TYPE_MAP = [
'char' => SchemaInterface::TYPE_CHAR,
'nchar' => SchemaInterface::TYPE_CHAR,
'varchar2' => SchemaInterface::TYPE_STRING,
'nvarchar2' => SchemaInterface::TYPE_STRING,
'clob' => SchemaInterface::TYPE_TEXT,
'nclob' => SchemaInterface::TYPE_TEXT,
'blob' => SchemaInterface::TYPE_BINARY,
'bfile' => SchemaInterface::TYPE_BINARY,
'long raw' => SchemaInterface::TYPE_BINARY,
'raw' => SchemaInterface::TYPE_BINARY,
'number' => SchemaInterface::TYPE_DECIMAL,
'binary_float' => SchemaInterface::TYPE_FLOAT, // 32 bit
'binary_double' => SchemaInterface::TYPE_DOUBLE, // 64 bit
'float' => SchemaInterface::TYPE_DOUBLE, // 126 bit
'date' => SchemaInterface::TYPE_DATE,
'interval day to second' => SchemaInterface::TYPE_TIME,
'timestamp' => SchemaInterface::TYPE_TIMESTAMP,
'timestamp with time zone' => SchemaInterface::TYPE_TIMESTAMP,
'timestamp with local time zone' => SchemaInterface::TYPE_TIMESTAMP,

/** Deprecated */
'long' => SchemaInterface::TYPE_TEXT,
];

protected function getType(string $dbType, array $info = []): string
{
$dbType = strtolower($dbType);

if ($dbType === 'number') {
$scale = isset($info['scale']) ? (int) $info['scale'] : null;

return match ($scale) {
null => SchemaInterface::TYPE_DOUBLE,
0 => SchemaInterface::TYPE_INTEGER,
default => SchemaInterface::TYPE_DECIMAL,
};
}

$dbType = preg_replace('/\([^)]+\)/', '', $dbType);

if ($dbType === 'interval day to second' && isset($info['precision']) && $info['precision'] > 0) {
return SchemaInterface::TYPE_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);
}
}
92 changes: 11 additions & 81 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Helper\DbArrayHelper;
use Yiisoft\Db\Oracle\Column\BinaryColumnSchema;
use Yiisoft\Db\Oracle\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;

Expand All @@ -29,10 +30,8 @@
use function is_array;
use function md5;
use function preg_match;
use function preg_replace;
use function serialize;
use function str_replace;
use function strtolower;
use function trim;

/**
Expand Down Expand Up @@ -68,38 +67,6 @@
*/
final class Schema extends AbstractPdoSchema
{
/**
* The mapping from physical column types (keys) to abstract column types (values).
*
* @link https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html
*
* @var string[]
*/
private const TYPE_MAP = [
'char' => self::TYPE_CHAR,
'nchar' => self::TYPE_CHAR,
'varchar2' => self::TYPE_STRING,
'nvarchar2' => self::TYPE_STRING,
'clob' => self::TYPE_TEXT,
'nclob' => self::TYPE_TEXT,
'blob' => self::TYPE_BINARY,
'bfile' => self::TYPE_BINARY,
'long raw' => self::TYPE_BINARY,
'raw' => self::TYPE_BINARY,
'number' => self::TYPE_DECIMAL,
'binary_float' => self::TYPE_FLOAT, // 32 bit
'binary_double' => self::TYPE_DOUBLE, // 64 bit
'float' => self::TYPE_DOUBLE, // 126 bit
'timestamp' => self::TYPE_TIMESTAMP,
'timestamp with time zone' => self::TYPE_TIMESTAMP,
'timestamp with local time zone' => self::TYPE_TIMESTAMP,
'date' => self::TYPE_DATE,
'interval day to second' => self::TYPE_TIME,

/** Deprecated */
'long' => self::TYPE_TEXT,
];

public function __construct(protected ConnectionInterface $db, SchemaCache $schemaCache, string $defaultSchema)
{
$this->defaultSchema = $defaultSchema;
Expand All @@ -111,6 +78,11 @@ public function createColumn(string $type, array|int|string $length = null): Col
return new Column($type, $length);
}

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

protected function resolveTableName(string $name): TableSchemaInterface
{
$resolvedName = new TableSchema();
Expand Down Expand Up @@ -459,32 +431,22 @@ protected function getTableSequenceName(string $tableName): string|null
private function loadColumnSchema(array $info): ColumnSchemaInterface
{
$dbType = $info['data_type'];
$type = $this->extractColumnType($dbType, $info);

$column = $this->createColumnSchema($type);
$column = $this->getColumnFactory()->fromDbType($dbType, [
'scale' => $info['data_scale'],
'precision' => $info['data_precision'],
]);
$column->name($info['column_name']);
$column->allowNull($info['nullable'] === 'Y');
$column->comment($info['column_comment']);
$column->primaryKey((bool) $info['is_pk']);
$column->autoIncrement($info['identity_column'] === 'YES');
$column->size((int) $info['data_length']);
$column->precision($info['data_precision'] !== null ? (int) $info['data_precision'] : null);
$column->scale($info['data_scale'] !== null ? (int) $info['data_scale'] : null);
$column->dbType($dbType);
$column->defaultValue($this->normalizeDefaultValue($info['data_default'], $column));

return $column;
}

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 Expand Up @@ -657,38 +619,6 @@ public function findUniqueIndexes(TableSchemaInterface $table): array
return $result;
}

/**
* Extracts the data type for the given column.
*
* @param string $dbType The database data type
* @param array $info Column information.
* @psalm-param ColumnInfoArray $info
*
* @return string The abstract column type.
*/
private function extractColumnType(string $dbType, array $info): string
{
$dbType = strtolower($dbType);

if ($dbType === 'number') {
$scale = $info['data_scale'] !== null ? (int) $info['data_scale'] : null;

return match ($scale) {
null => self::TYPE_DOUBLE,
0 => self::TYPE_INTEGER,
default => self::TYPE_DECIMAL,
};
}

$dbType = preg_replace('/\([^)]+\)/', '', $dbType);

if ($dbType === 'interval day to second' && $info['data_precision'] > 0) {
return self::TYPE_STRING;
}

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

/**
* Loads multiple types of constraints and returns the specified ones.
*
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\Oracle\Tests;

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

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

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

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

declare(strict_types=1);

namespace Yiisoft\Db\Oracle\Tests\Provider;

use Yiisoft\Db\Oracle\Column\BinaryColumnSchema;
use Yiisoft\Db\Schema\Column\DoubleColumnSchema;
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
['char', 'char', StringColumnSchema::class],
['nchar', 'char', StringColumnSchema::class],
['varchar2', 'string', StringColumnSchema::class],
['nvarchar2', 'string', StringColumnSchema::class],
['clob', 'text', StringColumnSchema::class],
['nclob', 'text', StringColumnSchema::class],
['long', 'text', StringColumnSchema::class],
['blob', 'binary', BinaryColumnSchema::class],
['bfile', 'binary', BinaryColumnSchema::class],
['long raw', 'binary', BinaryColumnSchema::class],
['raw', 'binary', BinaryColumnSchema::class],
['number', 'double', DoubleColumnSchema::class],
['binary_float', 'float', DoubleColumnSchema::class],
['binary_double', 'double', DoubleColumnSchema::class],
['float', 'double', DoubleColumnSchema::class],
['date', 'date', StringColumnSchema::class],
['interval day(0) to second', 'time', StringColumnSchema::class],
['timestamp', 'timestamp', StringColumnSchema::class],
['timestamp with time zone', 'timestamp', StringColumnSchema::class],
['timestamp with local time zone', 'timestamp', StringColumnSchema::class],
];
}

public static function definitions(): array
{
$definitions = parent::definitions();

$definitions['text'][0] = 'clob';
$definitions['text NOT NULL'][0] = 'clob NOT NULL';
$definitions['decimal(10,2)'][0] = 'number(10,2)';

unset($definitions['bigint UNSIGNED']);

return $definitions;
}
}
9 changes: 9 additions & 0 deletions tests/SchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Oracle\Column\ColumnFactory;
use Yiisoft\Db\Oracle\Schema;
use Yiisoft\Db\Oracle\Tests\Support\TestTrait;
use Yiisoft\Db\Tests\Common\CommonSchemaTest;
Expand Down Expand Up @@ -295,4 +296,12 @@ public function testNotConnectionPDO(): void

$schema->refresh();
}

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

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

0 comments on commit 40bb07d

Please sign in to comment.