Skip to content

Commit 40bb07d

Browse files
authored
Realize column factory (#276)
1 parent b15e5e5 commit 40bb07d

File tree

6 files changed

+188
-81
lines changed

6 files changed

+188
-81
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- Chg #272: Replace call of `SchemaInterface::getRawTableName()` to `QuoterInterface::getRawTableName()` (@Tigrov)
1212
- Enh #275: Refactor PHP type of `ColumnSchemaInterface` instances (@Tigrov)
1313
- Enh #277: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov)
14+
- Enh #276: Implement `ColumnFactory` class (@Tigrov)
1415

1516
## 1.3.0 March 21, 2024
1617

src/Column/ColumnFactory.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Db\Oracle\Column;
6+
7+
use Yiisoft\Db\Schema\Column\AbstractColumnFactory;
8+
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
9+
use Yiisoft\Db\Schema\SchemaInterface;
10+
11+
use function preg_replace;
12+
use function strtolower;
13+
14+
final class ColumnFactory extends AbstractColumnFactory
15+
{
16+
/**
17+
* The mapping from physical column types (keys) to abstract column types (values).
18+
*
19+
* @link https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html
20+
*
21+
* @var string[]
22+
*
23+
* @psalm-suppress MissingClassConstType
24+
*/
25+
private const TYPE_MAP = [
26+
'char' => SchemaInterface::TYPE_CHAR,
27+
'nchar' => SchemaInterface::TYPE_CHAR,
28+
'varchar2' => SchemaInterface::TYPE_STRING,
29+
'nvarchar2' => SchemaInterface::TYPE_STRING,
30+
'clob' => SchemaInterface::TYPE_TEXT,
31+
'nclob' => SchemaInterface::TYPE_TEXT,
32+
'blob' => SchemaInterface::TYPE_BINARY,
33+
'bfile' => SchemaInterface::TYPE_BINARY,
34+
'long raw' => SchemaInterface::TYPE_BINARY,
35+
'raw' => SchemaInterface::TYPE_BINARY,
36+
'number' => SchemaInterface::TYPE_DECIMAL,
37+
'binary_float' => SchemaInterface::TYPE_FLOAT, // 32 bit
38+
'binary_double' => SchemaInterface::TYPE_DOUBLE, // 64 bit
39+
'float' => SchemaInterface::TYPE_DOUBLE, // 126 bit
40+
'date' => SchemaInterface::TYPE_DATE,
41+
'interval day to second' => SchemaInterface::TYPE_TIME,
42+
'timestamp' => SchemaInterface::TYPE_TIMESTAMP,
43+
'timestamp with time zone' => SchemaInterface::TYPE_TIMESTAMP,
44+
'timestamp with local time zone' => SchemaInterface::TYPE_TIMESTAMP,
45+
46+
/** Deprecated */
47+
'long' => SchemaInterface::TYPE_TEXT,
48+
];
49+
50+
protected function getType(string $dbType, array $info = []): string
51+
{
52+
$dbType = strtolower($dbType);
53+
54+
if ($dbType === 'number') {
55+
$scale = isset($info['scale']) ? (int) $info['scale'] : null;
56+
57+
return match ($scale) {
58+
null => SchemaInterface::TYPE_DOUBLE,
59+
0 => SchemaInterface::TYPE_INTEGER,
60+
default => SchemaInterface::TYPE_DECIMAL,
61+
};
62+
}
63+
64+
$dbType = preg_replace('/\([^)]+\)/', '', $dbType);
65+
66+
if ($dbType === 'interval day to second' && isset($info['precision']) && $info['precision'] > 0) {
67+
return SchemaInterface::TYPE_STRING;
68+
}
69+
70+
return self::TYPE_MAP[$dbType] ?? SchemaInterface::TYPE_STRING;
71+
}
72+
73+
public function fromType(string $type, array $info = []): ColumnSchemaInterface
74+
{
75+
if ($type === SchemaInterface::TYPE_BINARY) {
76+
return (new BinaryColumnSchema($type))->load($info);
77+
}
78+
79+
return parent::fromType($type, $info);
80+
}
81+
}

src/Schema.php

Lines changed: 11 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
use Yiisoft\Db\Exception\NotSupportedException;
1818
use Yiisoft\Db\Expression\Expression;
1919
use Yiisoft\Db\Helper\DbArrayHelper;
20-
use Yiisoft\Db\Oracle\Column\BinaryColumnSchema;
20+
use Yiisoft\Db\Oracle\Column\ColumnFactory;
2121
use Yiisoft\Db\Schema\Builder\ColumnInterface;
22+
use Yiisoft\Db\Schema\Column\ColumnFactoryInterface;
2223
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
2324
use Yiisoft\Db\Schema\TableSchemaInterface;
2425

@@ -29,10 +30,8 @@
2930
use function is_array;
3031
use function md5;
3132
use function preg_match;
32-
use function preg_replace;
3333
use function serialize;
3434
use function str_replace;
35-
use function strtolower;
3635
use function trim;
3736

3837
/**
@@ -68,38 +67,6 @@
6867
*/
6968
final class Schema extends AbstractPdoSchema
7069
{
71-
/**
72-
* The mapping from physical column types (keys) to abstract column types (values).
73-
*
74-
* @link https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html
75-
*
76-
* @var string[]
77-
*/
78-
private const TYPE_MAP = [
79-
'char' => self::TYPE_CHAR,
80-
'nchar' => self::TYPE_CHAR,
81-
'varchar2' => self::TYPE_STRING,
82-
'nvarchar2' => self::TYPE_STRING,
83-
'clob' => self::TYPE_TEXT,
84-
'nclob' => self::TYPE_TEXT,
85-
'blob' => self::TYPE_BINARY,
86-
'bfile' => self::TYPE_BINARY,
87-
'long raw' => self::TYPE_BINARY,
88-
'raw' => self::TYPE_BINARY,
89-
'number' => self::TYPE_DECIMAL,
90-
'binary_float' => self::TYPE_FLOAT, // 32 bit
91-
'binary_double' => self::TYPE_DOUBLE, // 64 bit
92-
'float' => self::TYPE_DOUBLE, // 126 bit
93-
'timestamp' => self::TYPE_TIMESTAMP,
94-
'timestamp with time zone' => self::TYPE_TIMESTAMP,
95-
'timestamp with local time zone' => self::TYPE_TIMESTAMP,
96-
'date' => self::TYPE_DATE,
97-
'interval day to second' => self::TYPE_TIME,
98-
99-
/** Deprecated */
100-
'long' => self::TYPE_TEXT,
101-
];
102-
10370
public function __construct(protected ConnectionInterface $db, SchemaCache $schemaCache, string $defaultSchema)
10471
{
10572
$this->defaultSchema = $defaultSchema;
@@ -111,6 +78,11 @@ public function createColumn(string $type, array|int|string $length = null): Col
11178
return new Column($type, $length);
11279
}
11380

81+
public function getColumnFactory(): ColumnFactoryInterface
82+
{
83+
return new ColumnFactory();
84+
}
85+
11486
protected function resolveTableName(string $name): TableSchemaInterface
11587
{
11688
$resolvedName = new TableSchema();
@@ -459,32 +431,22 @@ protected function getTableSequenceName(string $tableName): string|null
459431
private function loadColumnSchema(array $info): ColumnSchemaInterface
460432
{
461433
$dbType = $info['data_type'];
462-
$type = $this->extractColumnType($dbType, $info);
463-
464-
$column = $this->createColumnSchema($type);
434+
$column = $this->getColumnFactory()->fromDbType($dbType, [
435+
'scale' => $info['data_scale'],
436+
'precision' => $info['data_precision'],
437+
]);
465438
$column->name($info['column_name']);
466439
$column->allowNull($info['nullable'] === 'Y');
467440
$column->comment($info['column_comment']);
468441
$column->primaryKey((bool) $info['is_pk']);
469442
$column->autoIncrement($info['identity_column'] === 'YES');
470443
$column->size((int) $info['data_length']);
471-
$column->precision($info['data_precision'] !== null ? (int) $info['data_precision'] : null);
472-
$column->scale($info['data_scale'] !== null ? (int) $info['data_scale'] : null);
473444
$column->dbType($dbType);
474445
$column->defaultValue($this->normalizeDefaultValue($info['data_default'], $column));
475446

476447
return $column;
477448
}
478449

479-
protected function createColumnSchemaFromType(string $type, bool $isUnsigned = false): ColumnSchemaInterface
480-
{
481-
if ($type === self::TYPE_BINARY) {
482-
return new BinaryColumnSchema($type);
483-
}
484-
485-
return parent::createColumnSchemaFromType($type, $isUnsigned);
486-
}
487-
488450
/**
489451
* Converts column's default value according to {@see ColumnSchema::phpType} after retrieval from the database.
490452
*
@@ -657,38 +619,6 @@ public function findUniqueIndexes(TableSchemaInterface $table): array
657619
return $result;
658620
}
659621

660-
/**
661-
* Extracts the data type for the given column.
662-
*
663-
* @param string $dbType The database data type
664-
* @param array $info Column information.
665-
* @psalm-param ColumnInfoArray $info
666-
*
667-
* @return string The abstract column type.
668-
*/
669-
private function extractColumnType(string $dbType, array $info): string
670-
{
671-
$dbType = strtolower($dbType);
672-
673-
if ($dbType === 'number') {
674-
$scale = $info['data_scale'] !== null ? (int) $info['data_scale'] : null;
675-
676-
return match ($scale) {
677-
null => self::TYPE_DOUBLE,
678-
0 => self::TYPE_INTEGER,
679-
default => self::TYPE_DECIMAL,
680-
};
681-
}
682-
683-
$dbType = preg_replace('/\([^)]+\)/', '', $dbType);
684-
685-
if ($dbType === 'interval day to second' && $info['data_precision'] > 0) {
686-
return self::TYPE_STRING;
687-
}
688-
689-
return self::TYPE_MAP[$dbType] ?? self::TYPE_STRING;
690-
}
691-
692622
/**
693623
* Loads multiple types of constraints and returns the specified ones.
694624
*

tests/ColumnFactoryTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Db\Oracle\Tests;
6+
7+
use Yiisoft\Db\Oracle\Tests\Support\TestTrait;
8+
use Yiisoft\Db\Tests\AbstractColumnFactoryTest;
9+
10+
/**
11+
* @group oracle
12+
*/
13+
final class ColumnFactoryTest extends AbstractColumnFactoryTest
14+
{
15+
use TestTrait;
16+
17+
/** @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\ColumnFactoryProvider::dbTypes */
18+
public function testFromDbType(string $dbType, string $expectedType, string $expectedInstanceOf): void
19+
{
20+
parent::testFromDbType($dbType, $expectedType, $expectedInstanceOf);
21+
}
22+
23+
/** @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\ColumnFactoryProvider::definitions */
24+
public function testFromDefinition(string $definition, string $expectedType, string $expectedInstanceOf, array $expectedInfo = []): void
25+
{
26+
parent::testFromDefinition($definition, $expectedType, $expectedInstanceOf, $expectedInfo);
27+
}
28+
29+
/** @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\ColumnFactoryProvider::types */
30+
public function testFromType(string $type, string $expectedType, string $expectedInstanceOf): void
31+
{
32+
parent::testFromType($type, $expectedType, $expectedInstanceOf);
33+
}
34+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Db\Oracle\Tests\Provider;
6+
7+
use Yiisoft\Db\Oracle\Column\BinaryColumnSchema;
8+
use Yiisoft\Db\Schema\Column\DoubleColumnSchema;
9+
use Yiisoft\Db\Schema\Column\StringColumnSchema;
10+
11+
final class ColumnFactoryProvider extends \Yiisoft\Db\Tests\Provider\ColumnFactoryProvider
12+
{
13+
public static function dbTypes(): array
14+
{
15+
return [
16+
// db type, expected abstract type, expected instance of
17+
['char', 'char', StringColumnSchema::class],
18+
['nchar', 'char', StringColumnSchema::class],
19+
['varchar2', 'string', StringColumnSchema::class],
20+
['nvarchar2', 'string', StringColumnSchema::class],
21+
['clob', 'text', StringColumnSchema::class],
22+
['nclob', 'text', StringColumnSchema::class],
23+
['long', 'text', StringColumnSchema::class],
24+
['blob', 'binary', BinaryColumnSchema::class],
25+
['bfile', 'binary', BinaryColumnSchema::class],
26+
['long raw', 'binary', BinaryColumnSchema::class],
27+
['raw', 'binary', BinaryColumnSchema::class],
28+
['number', 'double', DoubleColumnSchema::class],
29+
['binary_float', 'float', DoubleColumnSchema::class],
30+
['binary_double', 'double', DoubleColumnSchema::class],
31+
['float', 'double', DoubleColumnSchema::class],
32+
['date', 'date', StringColumnSchema::class],
33+
['interval day(0) to second', 'time', StringColumnSchema::class],
34+
['timestamp', 'timestamp', StringColumnSchema::class],
35+
['timestamp with time zone', 'timestamp', StringColumnSchema::class],
36+
['timestamp with local time zone', 'timestamp', StringColumnSchema::class],
37+
];
38+
}
39+
40+
public static function definitions(): array
41+
{
42+
$definitions = parent::definitions();
43+
44+
$definitions['text'][0] = 'clob';
45+
$definitions['text NOT NULL'][0] = 'clob NOT NULL';
46+
$definitions['decimal(10,2)'][0] = 'number(10,2)';
47+
48+
unset($definitions['bigint UNSIGNED']);
49+
50+
return $definitions;
51+
}
52+
}

tests/SchemaTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Yiisoft\Db\Exception\Exception;
1111
use Yiisoft\Db\Exception\InvalidConfigException;
1212
use Yiisoft\Db\Exception\NotSupportedException;
13+
use Yiisoft\Db\Oracle\Column\ColumnFactory;
1314
use Yiisoft\Db\Oracle\Schema;
1415
use Yiisoft\Db\Oracle\Tests\Support\TestTrait;
1516
use Yiisoft\Db\Tests\Common\CommonSchemaTest;
@@ -295,4 +296,12 @@ public function testNotConnectionPDO(): void
295296

296297
$schema->refresh();
297298
}
299+
300+
public function testGetColumnFactory(): void
301+
{
302+
$db = $this->getConnection();
303+
$factory = $db->getSchema()->getColumnFactory();
304+
305+
$this->assertInstanceOf(ColumnFactory::class, $factory);
306+
}
298307
}

0 commit comments

Comments
 (0)