Skip to content

Commit

Permalink
Merge branch 'master' into ease-local-testing
Browse files Browse the repository at this point in the history
  • Loading branch information
arogachev committed Nov 18, 2024
2 parents 98f1357 + 66bcb27 commit 5406fe1
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 165 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@
- 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)
- New #276: Implement `ColumnFactory` class (@Tigrov)
- New #276, #288: Implement `ColumnFactory` class (@Tigrov)
- Enh #279: Separate column type constants (@Tigrov)
- New #280: Realize `ColumnBuilder` class (@Tigrov)
- New #280, #291: Realize `ColumnBuilder` class (@Tigrov)
- Enh #281: Update according changes in `ColumnSchemaInterface` (@Tigrov)
- New #282: Add `ColumnDefinitionBuilder` class (@Tigrov)
- New #282, #291: Add `ColumnDefinitionBuilder` class (@Tigrov)
- Bug #285: Fix `DMLQueryBuilder::insertBatch()` method (@Tigrov)
- Enh #283: Refactor `Dsn` class (@Tigrov)
- Enh #286: Use constructor to create columns and initialize properties (@Tigrov)
- Enh #288: Refactor `Schema::findColumns()` method (@Tigrov)
- Enh #289: Refactor `Schema::normalizeDefaultValue()` method and move it to `ColumnFactory` class (@Tigrov)

## 1.3.0 March 21, 2024

Expand Down
20 changes: 0 additions & 20 deletions src/Column/ColumnBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,6 @@

final class ColumnBuilder extends \Yiisoft\Db\Schema\Column\ColumnBuilder
{
public static function tinyint(int|null $size = 3): ColumnSchemaInterface
{
return parent::tinyint($size);
}

public static function smallint(int|null $size = 5): ColumnSchemaInterface
{
return parent::smallint($size);
}

public static function integer(int|null $size = 10): ColumnSchemaInterface
{
return parent::integer($size);
}

public static function bigint(int|null $size = 20): ColumnSchemaInterface
{
return parent::bigint($size);
}

public static function binary(int|null $size = null): ColumnSchemaInterface
{
return new BinaryColumnSchema(ColumnType::BINARY, size: $size);
Expand Down
16 changes: 9 additions & 7 deletions src/Column/ColumnDefinitionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ final class ColumnDefinitionBuilder extends AbstractColumnDefinitionBuilder
protected const GENERATE_UUID_EXPRESSION = 'sys_guid()';

protected const TYPES_WITH_SIZE = [
'char',
'nchar',
'character',
'varchar',
'varchar2',
'nvarchar2',
'number',
Expand All @@ -27,8 +31,6 @@ final class ColumnDefinitionBuilder extends AbstractColumnDefinitionBuilder
'interval day(0) to second',
'raw',
'urowid',
'char',
'nchar',
];

protected const TYPES_WITH_SCALE = [
Expand Down Expand Up @@ -67,7 +69,7 @@ protected function getDbType(ColumnSchemaInterface $column): string
$size = $column->getSize();

/** @psalm-suppress DocblockTypeContradiction */
return match ($column->getType()) {
return $column->getDbType() ?? match ($column->getType()) {
ColumnType::BOOLEAN => 'number(1)',
ColumnType::BIT => match (true) {
$size === null => 'number(38)',
Expand All @@ -83,17 +85,17 @@ protected function getDbType(ColumnSchemaInterface $column): string
ColumnType::DECIMAL => 'number(' . ($size ?? 10) . ',' . ($column->getScale() ?? 0) . ')',
ColumnType::MONEY => 'number(' . ($size ?? 19) . ',' . ($column->getScale() ?? 4) . ')',
ColumnType::CHAR => 'char',
ColumnType::STRING => 'varchar2',
ColumnType::STRING => 'varchar2(' . ($size ?? 255) . ')',
ColumnType::TEXT => 'clob',
ColumnType::BINARY => 'blob',
ColumnType::UUID => 'raw(16)',
ColumnType::DATETIME => 'timestamp',
ColumnType::TIMESTAMP => 'timestamp',
ColumnType::DATE => 'date',
ColumnType::TIME => 'interval day(0) to second',
ColumnType::ARRAY => 'json',
ColumnType::STRUCTURED => 'json',
ColumnType::JSON => 'json',
ColumnType::ARRAY => 'clob',
ColumnType::STRUCTURED => 'clob',
ColumnType::JSON => 'clob',
default => 'varchar2',
};
}
Expand Down
21 changes: 11 additions & 10 deletions src/Column/ColumnFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;

use function preg_replace;
use function rtrim;
use function strtolower;

final class ColumnFactory extends AbstractColumnFactory
Expand All @@ -19,12 +20,13 @@ final class ColumnFactory extends AbstractColumnFactory
* @link https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html
*
* @var string[]
*
* @psalm-suppress MissingClassConstType
* @psalm-var array<string, ColumnType::*>
*/
private const TYPE_MAP = [
protected const TYPE_MAP = [
'char' => ColumnType::CHAR,
'nchar' => ColumnType::CHAR,
'character' => ColumnType::CHAR,
'varchar' => ColumnType::STRING,
'varchar2' => ColumnType::STRING,
'nvarchar2' => ColumnType::STRING,
'clob' => ColumnType::TEXT,
Expand Down Expand Up @@ -65,21 +67,20 @@ protected function getType(string $dbType, array $info = []): string
return ColumnType::STRING;
}

return self::TYPE_MAP[$dbType] ?? ColumnType::STRING;
return parent::getType($dbType, $info);
}

public function fromType(string $type, array $info = []): ColumnSchemaInterface
protected function getColumnClass(string $type, array $info = []): string
{
if ($type === ColumnType::BINARY) {
unset($info['type']);
return new BinaryColumnSchema($type, ...$info);
return BinaryColumnSchema::class;
}

return parent::fromType($type, $info);
return parent::getColumnClass($type, $info);
}

protected function isDbType(string $dbType): bool
protected function normalizeNotNullDefaultValue(string $defaultValue, ColumnSchemaInterface $column): mixed
{
return isset(self::TYPE_MAP[$dbType]);
return parent::normalizeNotNullDefaultValue(rtrim($defaultValue), $column);
}
}
128 changes: 56 additions & 72 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Throwable;
use Yiisoft\Db\Cache\SchemaCache;
use Yiisoft\Db\Connection\ConnectionInterface;
use Yiisoft\Db\Constant\ColumnType;
use Yiisoft\Db\Constraint\CheckConstraint;
use Yiisoft\Db\Constraint\Constraint;
use Yiisoft\Db\Constraint\ForeignKeyConstraint;
Expand All @@ -16,7 +15,6 @@
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Helper\DbArrayHelper;
use Yiisoft\Db\Oracle\Column\ColumnFactory;
use Yiisoft\Db\Schema\Builder\ColumnInterface;
Expand All @@ -30,24 +28,23 @@
use function implode;
use function is_array;
use function md5;
use function preg_match;
use function serialize;
use function str_replace;
use function trim;

/**
* Implements the Oracle Server specific schema, supporting Oracle Server 11C and above.
*
* @psalm-type ColumnInfoArray = array{
* @psalm-type ColumnArray = array{
* column_name: string,
* data_type: string,
* data_scale: string|null,
* identity_column: string,
* size: string|null,
* nullable: string,
* data_default: string|null,
* is_pk: string|null,
* identity_column: string,
* column_comment: string|null
* constraint_type: string|null,
* column_comment: string|null,
* schema: string,
* table: string
* }
*
* @psalm-type ConstraintArray = array<
Expand Down Expand Up @@ -334,6 +331,9 @@ protected function loadTableDefaultValues(string $tableName): array
*/
protected function findColumns(TableSchemaInterface $table): bool
{
$schemaName = $table->getSchemaName();
$tableName = $table->getName();

$sql = <<<SQL
SELECT
A.COLUMN_NAME,
Expand All @@ -343,30 +343,45 @@ protected function findColumns(TableSchemaInterface $table): bool
(CASE WHEN A.CHAR_LENGTH > 0 THEN A.CHAR_LENGTH ELSE A.DATA_PRECISION END) AS "size",
A.NULLABLE,
A.DATA_DEFAULT,
(
SELECT COUNT(*)
FROM ALL_CONSTRAINTS AC
INNER JOIN ALL_CONS_COLUMNS ACC ON ACC.CONSTRAINT_NAME=AC.CONSTRAINT_NAME
WHERE
AC.OWNER = A.OWNER
AND AC.TABLE_NAME = B.OBJECT_NAME
AND ACC.COLUMN_NAME = A.COLUMN_NAME
AND AC.CONSTRAINT_TYPE = 'P'
) AS IS_PK,
AC.CONSTRAINT_TYPE,
COM.COMMENTS AS COLUMN_COMMENT
FROM ALL_TAB_COLUMNS A
INNER JOIN ALL_OBJECTS B ON B.OWNER = A.OWNER AND LTRIM(B.OBJECT_NAME) = LTRIM(A.TABLE_NAME)
LEFT JOIN ALL_COL_COMMENTS COM ON (A.OWNER = COM.OWNER AND A.TABLE_NAME = COM.TABLE_NAME AND A.COLUMN_NAME = COM.COLUMN_NAME)
WHERE
A.OWNER = :schemaName
INNER JOIN ALL_OBJECTS B
ON B.OWNER = A.OWNER
AND B.OBJECT_NAME = A.TABLE_NAME
LEFT JOIN ALL_COL_COMMENTS COM
ON COM.OWNER = A.OWNER
AND COM.TABLE_NAME = A.TABLE_NAME
AND COM.COLUMN_NAME = A.COLUMN_NAME
LEFT JOIN ALL_CONSTRAINTS AC
ON AC.OWNER = A.OWNER
AND AC.TABLE_NAME = A.TABLE_NAME
AND (AC.CONSTRAINT_TYPE = 'P'
OR AC.CONSTRAINT_TYPE = 'U'
AND (
SELECT COUNT(*)
FROM ALL_CONS_COLUMNS UCC
WHERE UCC.CONSTRAINT_NAME = AC.CONSTRAINT_NAME
AND UCC.TABLE_NAME = AC.TABLE_NAME
AND UCC.OWNER = AC.OWNER
) = 1
)
AND AC.CONSTRAINT_NAME IN (
SELECT ACC.CONSTRAINT_NAME
FROM ALL_CONS_COLUMNS ACC
WHERE ACC.OWNER = A.OWNER
AND ACC.TABLE_NAME = A.TABLE_NAME
AND ACC.COLUMN_NAME = A.COLUMN_NAME
)
WHERE A.OWNER = :schemaName
AND A.TABLE_NAME = :tableName
AND B.OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')
AND B.OBJECT_NAME = :tableName
ORDER BY A.COLUMN_ID
SQL;

$columns = $this->db->createCommand($sql, [
':tableName' => $table->getName(),
':schemaName' => $table->getSchemaName(),
':schemaName' => $schemaName,
':tableName' => $tableName,
])->queryAll();

if ($columns === []) {
Expand All @@ -375,9 +390,12 @@ protected function findColumns(TableSchemaInterface $table): bool

/** @psalm-var string[][] $info */
foreach ($columns as $info) {
/** @psalm-var ColumnInfoArray $info */
$info = array_change_key_case($info);

$info['schema'] = $schemaName;
$info['table'] = $tableName;

/** @psalm-var ColumnArray $info */
$column = $this->loadColumnSchema($info);

$table->column($info['column_name'], $column);
Expand Down Expand Up @@ -422,57 +440,23 @@ protected function getTableSequenceName(string $tableName): string|null
*
* @return ColumnSchemaInterface The column schema object.
*
* @psalm-param ColumnInfoArray $info The column information.
* @psalm-param ColumnArray $info The column information.
*/
private function loadColumnSchema(array $info): ColumnSchemaInterface
{
$columnFactory = $this->db->getSchema()->getColumnFactory();

$dbType = $info['data_type'];
$column = $columnFactory->fromDbType($dbType, [
return $this->getColumnFactory()->fromDbType($info['data_type'], [
'autoIncrement' => $info['identity_column'] === 'YES',
'comment' => $info['column_comment'],
'defaultValueRaw' => $info['data_default'],
'name' => $info['column_name'],
'notNull' => $info['nullable'] !== 'Y',
'primaryKey' => $info['constraint_type'] === 'P',
'scale' => $info['data_scale'] !== null ? (int) $info['data_scale'] : null,
'schema' => $info['schema'],
'size' => $info['size'] !== null ? (int) $info['size'] : null,
'table' => $info['table'],
'unique' => $info['constraint_type'] === 'U',
]);
/** @psalm-suppress DeprecatedMethod */
$column->name($info['column_name']);
$column->notNull($info['nullable'] !== 'Y');
$column->comment($info['column_comment']);
$column->primaryKey((bool) $info['is_pk']);
$column->autoIncrement($info['identity_column'] === 'YES');
$column->defaultValue($this->normalizeDefaultValue($info['data_default'], $column));

return $column;
}

/**
* Converts column's default value according to {@see ColumnSchema::phpType} 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.
*/
private function normalizeDefaultValue(string|null $defaultValue, ColumnSchemaInterface $column): mixed
{
if ($defaultValue === null || $column->isPrimaryKey()) {
return null;
}

$defaultValue = trim($defaultValue);

if ($defaultValue === 'NULL') {
return null;
}

if ($column->getType() === ColumnType::TIMESTAMP && $defaultValue === 'CURRENT_TIMESTAMP') {
return new Expression($defaultValue);
}

if (preg_match("/^'(.*)'$/s", $defaultValue, $matches) === 1) {
$defaultValue = str_replace("''", "'", $matches[1]);
}

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

/**
Expand Down
Loading

0 comments on commit 5406fe1

Please sign in to comment.