diff --git a/CHANGELOG.md b/CHANGELOG.md index 7713bf34..5644354c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - Enh #336: Provide `yiisoft/db-implementation` virtual package (@vjik) - Enh #340: Adapt to `Param` refactoring in `yiisoft/db` package (@vjik) - Enh #341, #342, #345: Adapt to conditions refactoring in `yiisoft/db` package (@vjik) +- Enh #348: Remove `TableSchema` class and refactor `Schema` class (@Tigrov) ## 1.3.0 March 21, 2024 diff --git a/src/Command.php b/src/Command.php index 7a70fbcb..be6effd6 100644 --- a/src/Command.php +++ b/src/Command.php @@ -11,6 +11,7 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Query\QueryInterface; use Yiisoft\Db\QueryBuilder\AbstractQueryBuilder; +use Yiisoft\Db\Schema\TableSchema; use function array_keys; use function array_map; diff --git a/src/Schema.php b/src/Schema.php index eae88793..69af3061 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -15,12 +15,13 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Helper\DbArrayHelper; use Yiisoft\Db\Schema\Column\ColumnInterface; +use Yiisoft\Db\Schema\SchemaInterface; +use Yiisoft\Db\Schema\TableSchema; use Yiisoft\Db\Schema\TableSchemaInterface; use function array_change_key_case; use function array_column; use function array_map; -use function implode; use function in_array; use function preg_replace; use function strtolower; @@ -59,27 +60,19 @@ */ final class Schema extends AbstractPdoSchema { - public function __construct(protected ConnectionInterface $db, SchemaCache $schemaCache, string $defaultSchema) + protected function findConstraints(TableSchemaInterface $table): void { - $this->defaultSchema = $defaultSchema; - parent::__construct($db, $schemaCache); + $tableName = $this->resolveFullName($table->getName(), $table->getSchemaName()); + + $table->checks(...$this->getTableMetadata($tableName, SchemaInterface::CHECKS)); + $table->foreignKeys(...$this->getTableMetadata($tableName, SchemaInterface::FOREIGN_KEYS)); + $table->indexes(...$this->getTableMetadata($tableName, SchemaInterface::INDEXES)); } - protected function resolveTableName(string $name): TableSchemaInterface + public function __construct(protected ConnectionInterface $db, SchemaCache $schemaCache, string $defaultSchema) { - $resolvedName = new TableSchema(); - - $parts = $this->db->getQuoter()->getTableNameParts($name); - - $resolvedName->name($parts['name']); - $resolvedName->schemaName($parts['schemaName'] ?? $this->defaultSchema); - - $resolvedName->fullName( - $resolvedName->getSchemaName() !== $this->defaultSchema ? - implode('.', $parts) : $resolvedName->getName() - ); - - return $resolvedName; + $this->defaultSchema = $defaultSchema; + parent::__construct($db, $schemaCache); } /** @@ -109,7 +102,7 @@ protected function findTableComment(TableSchemaInterface $tableSchema): void SQL; $comment = $this->db->createCommand($sql, [ - ':schemaName' => $tableSchema->getSchemaName(), + ':schemaName' => $tableSchema->getSchemaName() ?: $this->defaultSchema, ':tableName' => $tableSchema->getName(), ])->queryScalar(); @@ -216,23 +209,19 @@ protected function loadResultColumn(array $metadata): ColumnInterface|null protected function loadTableSchema(string $name): TableSchemaInterface|null { - $table = $this->resolveTableName($name); - $this->findTableComment($table); + $table = new TableSchema(...$this->db->getQuoter()->getTableNameParts($name)); if ($this->findColumns($table)) { + $this->findTableComment($table); $this->findConstraints($table); + $table->sequenceName($this->getTableSequenceName($table->getName())); + return $table; } return null; } - protected function loadTablePrimaryKey(string $tableName): Index|null - { - /** @var Index|null */ - return $this->loadTableConstraints($tableName, self::PRIMARY_KEY); - } - protected function loadTableForeignKeys(string $tableName): array { /** @var ForeignKey[] */ @@ -277,7 +266,7 @@ protected function loadTableIndexes(string $tableName): array } /** @var string[] $columnNames */ - $result[] = new Index( + $result[$name] = new Index( $name, $columnNames, (bool) $index[0]['is_unique'], @@ -288,12 +277,6 @@ protected function loadTableIndexes(string $tableName): array return $result; } - protected function loadTableUniques(string $tableName): array - { - /** @var Index[] */ - return $this->loadTableConstraints($tableName, self::UNIQUES); - } - protected function loadTableChecks(string $tableName): array { /** @var Check[] */ @@ -317,7 +300,7 @@ protected function loadTableDefaultValues(string $tableName): array */ protected function findColumns(TableSchemaInterface $table): bool { - $schemaName = $table->getSchemaName(); + $schemaName = $table->getSchemaName() ?: $this->defaultSchema; $tableName = $table->getName(); $sql = <<db->getColumnFactory()->fromDbType($dbType, $columnInfo); } - /** - * Finds constraints and fills them into TableSchemaInterface object passed. - * - * @psalm-suppress PossiblyNullArrayOffset - */ - protected function findConstraints(TableSchemaInterface $table): void - { - $sql = <<db->createCommand( - $sql, - [':tableName' => $table->getName(), ':schemaName' => $table->getSchemaName()] - )->queryAll(); - - $constraints = []; - - foreach ($rows as $row) { - /** @psalm-var string[] $row */ - $row = array_change_key_case($row); - - if ($row['constraint_type'] === 'P') { - $table->getColumns()[$row['column_name']]->primaryKey(true); - $table->primaryKey($row['column_name']); - - if (empty($table->getSequenceName())) { - $table->sequenceName($this->getTableSequenceName($table->getName())); - } - } - - if ($row['constraint_type'] !== 'R') { - /** - * This condition isn't checked in `WHERE` because of an Oracle Bug: - * - * @link https://github.com/yiisoft/yii2/pull/8844 - */ - continue; - } - - $name = $row['constraint_name']; - - if (!isset($constraints[$name])) { - $constraints[$name] = [ - 'tableName' => $row['table_ref'], - 'columns' => [], - ]; - } - - $constraints[$name]['columns'][$row['column_name']] = $row['column_ref']; - } - - foreach ($constraints as $index => $constraint) { - $table->foreignKey($index, [$constraint['tableName'], ...$constraint['columns']]); - } - } - - public function findUniqueIndexes(TableSchemaInterface $table): array - { - $query = <<db->createCommand( - $query, - [':tableName' => $table->getName(), ':schemaName' => $table->getschemaName()] - )->queryAll(); - - /** @psalm-var array $rows */ - foreach ($rows as $row) { - $result[$row['INDEX_NAME']][] = $row['COLUMN_NAME']; - } - - return $result; - } - /** * Loads multiple types of constraints and returns the specified ones. * * @param string $tableName The table name. * @param string $returnType The return type: - * - primaryKey * - foreignKeys - * - uniques * - checks * - * @return Check[]|ForeignKey[]|Index|Index[]|null Constraints. + * @return Check[]|ForeignKey[] Constraints. */ - private function loadTableConstraints(string $tableName, string $returnType): array|Index|null + private function loadTableConstraints(string $tableName, string $returnType): array { $sql = << null, self::FOREIGN_KEYS => [], - self::UNIQUES => [], self::CHECKS => [], ]; @@ -642,41 +504,21 @@ private function loadTableConstraints(string $tableName, string $returnType): ar * @psalm-var ConstraintArray $constraint */ foreach ($names as $name => $constraint) { - switch ($type) { - case 'P': - $result[self::PRIMARY_KEY] = new Index( - $name, - array_column($constraint, 'column_name'), - true, - true, - ); - break; - case 'R': - $result[self::FOREIGN_KEYS][] = new ForeignKey( - $name, - array_column($constraint, 'column_name'), - $constraint[0]['foreign_table_schema'], - $constraint[0]['foreign_table_name'], - array_column($constraint, 'foreign_column_name'), - $constraint[0]['on_delete'], - null, - ); - break; - case 'U': - $result[self::UNIQUES][] = new Index( - $name, - array_column($constraint, 'column_name'), - true, - ); - break; - case 'C': - $result[self::CHECKS][] = new Check( - $name, - array_column($constraint, 'column_name'), - $constraint[0]['check_expr'], - ); - break; - } + match ($type) { + 'R' => $result[self::FOREIGN_KEYS][$name] = new ForeignKey( + $name, + array_column($constraint, 'column_name'), + $constraint[0]['foreign_table_schema'], + $constraint[0]['foreign_table_name'], + array_column($constraint, 'foreign_column_name'), + $constraint[0]['on_delete'], + ), + 'C' => $result[self::CHECKS][$name] = new Check( + $name, + array_column($constraint, 'column_name'), + $constraint[0]['check_expr'], + ), + }; } } diff --git a/src/TableSchema.php b/src/TableSchema.php deleted file mode 100644 index 3272df32..00000000 --- a/src/TableSchema.php +++ /dev/null @@ -1,18 +0,0 @@ -foreignKeys[] = $to; - } -} diff --git a/tests/CommandTest.php b/tests/CommandTest.php index d010ddd0..9d9ef251 100644 --- a/tests/CommandTest.php +++ b/tests/CommandTest.php @@ -610,15 +610,12 @@ public function testCreateSearchIndex() $command->createTable($tableName, ['col1' => ColumnBuilder::text()])->execute(); $command->createIndex($tableName, $indexName, ['col1'], IndexType::SEARCH)->execute(); - $indexes = $schema->getTableIndexes($tableName); - Assert::setPropertyValue($indexes[1], 'name', ''); - - $this->assertEquals( + Assert::constraintsEquals( [ new Index($indexName, ['col1']), new Index('', [], true), ], - $indexes, + $schema->getTableIndexes($tableName), ); $db->close(); diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index 25a55893..478822b7 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -7,9 +7,9 @@ use PHPUnit\Framework\Attributes\DataProviderExternal; use Yiisoft\Db\Command\CommandInterface; use Yiisoft\Db\Connection\ConnectionInterface; +use Yiisoft\Db\Constant\ReferentialAction; +use Yiisoft\Db\Constraint\ForeignKey; use Yiisoft\Db\Driver\Pdo\PdoConnectionInterface; -use Yiisoft\Db\Exception\Exception; -use Yiisoft\Db\Exception\InvalidConfigException; use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Oracle\Schema; use Yiisoft\Db\Oracle\Tests\Provider\SchemaProvider; @@ -22,16 +22,12 @@ /** * @group oracle - * - * @psalm-suppress PropertyNotSetInConstructor */ final class SchemaTest extends CommonSchemaTest { use TestTrait; - /** - * @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\SchemaProvider::columns - */ + #[DataProviderExternal(SchemaProvider::class, 'columns')] public function testColumns(array $columns, string $tableName = 'type'): void { $db = $this->getConnection(); @@ -48,10 +44,6 @@ public function testColumns(array $columns, string $tableName = 'type'): void parent::testColumns($columns, $tableName); } - /** - * @throws Exception - * @throws InvalidConfigException - */ public function testCompositeFk(): void { $db = $this->getConnection(true); @@ -59,22 +51,23 @@ public function testCompositeFk(): void $schema = $db->getSchema(); $table = $schema->getTableSchema('composite_fk'); - $this->assertNotNull($table); - - $fk = $table->getForeignKeys(); - - $this->assertCount(1, $fk); - $this->assertSame('order_item', $fk[0][0]); - $this->assertSame('order_id', $fk[0]['order_id']); - $this->assertSame('item_id', $fk[0]['item_id']); + $this->assertEquals( + [ + 'FK_composite_fk_order_item' => new ForeignKey( + 'FK_composite_fk_order_item', + ['order_id', 'item_id'], + 'SYSTEM', + 'order_item', + ['order_id', 'item_id'], + ReferentialAction::CASCADE, + ), + ], + $table->getForeignKeys(), + ); $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - */ public function testGetDefaultSchema(): void { $db = $this->getConnection(); @@ -94,11 +87,6 @@ public function testGetSchemaDefaultValues(): void parent::testGetSchemaDefaultValues(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws NotSupportedException - */ public function testGetSchemaNames(): void { $db = $this->getConnection(true); @@ -114,11 +102,6 @@ public function testGetSchemaNames(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - * @throws NotSupportedException - */ public function testGetTableNamesWithSchema(): void { $db = $this->getConnection(true); @@ -164,10 +147,6 @@ public function testGetTableNamesWithSchema(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - */ public function testGetViewNames(): void { $db = $this->getConnection(true); @@ -180,10 +159,6 @@ public function testGetViewNames(): void $db->close(); } - /** - * @throws Exception - * @throws InvalidConfigException - */ public function testGetViewNamesWithSchema(): void { $db = $this->getConnection(true); @@ -196,41 +171,25 @@ public function testGetViewNamesWithSchema(): void $db->close(); } - /** - * @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\SchemaProvider::constraints - * - * @throws Exception - */ + #[DataProviderExternal(SchemaProvider::class, 'constraints')] public function testTableSchemaConstraints(string $tableName, string $type, mixed $expected): void { parent::testTableSchemaConstraints($tableName, $type, $expected); } - /** - * @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\SchemaProvider::constraints - * - * @throws Exception - */ + #[DataProviderExternal(SchemaProvider::class, 'constraints')] public function testTableSchemaConstraintsWithPdoLowercase(string $tableName, string $type, mixed $expected): void { parent::testTableSchemaConstraintsWithPdoLowercase($tableName, $type, $expected); } - /** - * @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\SchemaProvider::constraints - * - * @throws Exception - */ + #[DataProviderExternal(SchemaProvider::class, 'constraints')] public function testTableSchemaConstraintsWithPdoUppercase(string $tableName, string $type, mixed $expected): void { parent::testTableSchemaConstraintsWithPdoUppercase($tableName, $type, $expected); } - /** - * @dataProvider \Yiisoft\Db\Oracle\Tests\Provider\SchemaProvider::tableSchemaWithDbSchemes - * - * @throws Exception - */ + #[DataProviderExternal(SchemaProvider::class, 'tableSchemaWithDbSchemes')] public function testTableSchemaWithDbSchemes( string $tableName, string $expectedTableName, @@ -257,7 +216,7 @@ function ($params) use ($expectedTableName, $expectedSchemaName) { ) ->willReturn($commandMock); $schema = new Schema($mockDb, DbHelper::getSchemaCache(), 'dbo'); - $schema->getTablePrimaryKey($tableName); + $schema->getTablePrimaryKey($tableName, true); $db->close(); } diff --git a/tests/Support/Fixture/oci.sql b/tests/Support/Fixture/oci.sql index 6f61a007..9610290e 100644 --- a/tests/Support/Fixture/oci.sql +++ b/tests/Support/Fixture/oci.sql @@ -137,7 +137,7 @@ CREATE TABLE "composite_fk" ( "order_id" integer NOT NULL, "item_id" integer NOT NULL, CONSTRAINT "composite_fk_PK" PRIMARY KEY ("id") ENABLE, - CONSTRAINT FK_composite_fk_order_item FOREIGN KEY ("order_id", "item_id") + CONSTRAINT "FK_composite_fk_order_item" FOREIGN KEY ("order_id", "item_id") REFERENCES "order_item" ("order_id", "item_id") ON DELETE CASCADE );