Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
- New #348: Realize `Schema::loadResultColumn()` method (@Tigrov)
- New #354: Add `FOR` clause to query (@vjik)
- New #355: Use `DateTimeColumn` class for datetime column types (@Tigrov)
- Enh #358: Refactor constraints (@Tigrov)
- Enh #358, #372: Refactor constraints (@Tigrov)
- New #356, #357: Implement `DMLQueryBuilder::upsertReturning()` method (@Tigrov)
- Enh #356, #357: Refactor `Command::insertWithReturningPks()` and `DMLQueryBuilder::upsert()` methods (@Tigrov)
- Enh #360, #361: Implement `DMLQueryBuilder::insertReturningPks()` method (@Tigrov)
Expand Down
51 changes: 26 additions & 25 deletions src/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
use Throwable;
use Yiisoft\Db\Constant\ColumnType;
use Yiisoft\Db\Constant\ReferentialAction;
use Yiisoft\Db\Constraint\CheckConstraint;
use Yiisoft\Db\Constraint\ForeignKeyConstraint;
use Yiisoft\Db\Constraint\IndexConstraint;
use Yiisoft\Db\Constraint\Check;
use Yiisoft\Db\Constraint\ForeignKey;
use Yiisoft\Db\Constraint\Index;
use Yiisoft\Db\Driver\Pdo\AbstractPdoSchema;
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
Expand Down Expand Up @@ -92,9 +92,9 @@ protected function loadTableSchema(string $name): TableSchemaInterface|null
return null;
}

protected function loadTablePrimaryKey(string $tableName): IndexConstraint|null
protected function loadTablePrimaryKey(string $tableName): Index|null
{
/** @var IndexConstraint|null */
/** @var Index|null */
return $this->loadTableConstraints($tableName, self::PRIMARY_KEY);
}

Expand All @@ -113,21 +113,22 @@ protected function loadTableForeignKeys(string $tableName): array
*/
foreach ($foreignKeysById as $id => $foreignKey) {
if ($foreignKey[0]['to'] === null) {
/** @var IndexConstraint $primaryKey */
/** @var Index $primaryKey */
$primaryKey = $this->getTablePrimaryKey($table);
$foreignColumnNames = $primaryKey->getColumnNames();
$foreignColumnNames = $primaryKey->columnNames;
} else {
/** @var string[] $foreignColumnNames */
$foreignColumnNames = array_column($foreignKey, 'to');
}

$result[] = new ForeignKeyConstraint(
$result[] = new ForeignKey(
(string) $id,
array_column($foreignKey, 'from'),
'',
$table,
$foreignColumnNames,
$foreignKey[0]['on_update'],
$foreignKey[0]['on_delete'],
$foreignKey[0]['on_update'],
);
}
}
Expand All @@ -137,13 +138,13 @@ protected function loadTableForeignKeys(string $tableName): array

protected function loadTableIndexes(string $tableName): array
{
/** @var IndexConstraint[] */
/** @var Index[] */
return $this->loadTableConstraints($tableName, self::INDEXES);
}

protected function loadTableUniques(string $tableName): array
{
/** @var IndexConstraint[] */
/** @var Index[] */
return $this->loadTableConstraints($tableName, self::UNIQUES);
}

Expand Down Expand Up @@ -181,7 +182,7 @@ protected function loadTableChecks(string $tableName): array
$name = $sqlToken?->getContent() ?? '';
}

$result[] = new CheckConstraint($name, expression: $checkSql);
$result[] = new Check($name, expression: $checkSql);
}
}

Expand Down Expand Up @@ -242,18 +243,18 @@ protected function findColumns(TableSchemaInterface $table): bool
*/
protected function findConstraints(TableSchemaInterface $table): void
{
/** @psalm-var ForeignKeyConstraint[] $foreignKeysList */
/** @psalm-var ForeignKey[] $foreignKeysList */
$foreignKeysList = $this->getTableForeignKeys($table->getName(), true);

foreach ($foreignKeysList as $foreignKey) {
/** @var array<string> $columnNames */
$columnNames = $foreignKey->getColumnNames();
$columnNames = array_combine($columnNames, $foreignKey->getForeignColumnNames());
$columnNames = $foreignKey->columnNames;
$columnNames = array_combine($columnNames, $foreignKey->foreignColumnNames);

$foreignReference = [$foreignKey->getForeignTableName(), ...$columnNames];
$foreignReference = [$foreignKey->foreignTableName, ...$columnNames];

/** @psalm-suppress InvalidCast */
$table->foreignKey($foreignKey->getName(), $foreignReference);
$table->foreignKey($foreignKey->name, $foreignReference);
}
}

Expand Down Expand Up @@ -363,9 +364,9 @@ private function loadTableColumnsInfo(string $tableName): array
* @param string $tableName The table name.
* @param string $returnType Return type: (primaryKey, indexes, uniques).
*
* @psalm-return IndexConstraint[]|IndexConstraint|null
* @psalm-return Index[]|Index|null
*/
private function loadTableConstraints(string $tableName, string $returnType): array|IndexConstraint|null
private function loadTableConstraints(string $tableName, string $returnType): array|Index|null
{
$indexList = $this->getPragmaIndexList($tableName);
/** @psalm-var IndexListInfo[] $indexes */
Expand All @@ -380,12 +381,12 @@ private function loadTableConstraints(string $tableName, string $returnType): ar
$columns = $this->getPragmaIndexInfo($index['name']);

if ($index['origin'] === 'pk') {
$result[self::PRIMARY_KEY] = new IndexConstraint('', array_column($columns, 'name'), true, true);
$result[self::PRIMARY_KEY] = new Index('', array_column($columns, 'name'), true, true);
} elseif ($index['origin'] === 'u') {
$result[self::UNIQUES][] = new IndexConstraint($index['name'], array_column($columns, 'name'), true);
$result[self::UNIQUES][] = new Index($index['name'], array_column($columns, 'name'), true);
}

$result[self::INDEXES][] = new IndexConstraint(
$result[self::INDEXES][] = new Index(
$index['name'],
array_column($columns, 'name'),
(bool) $index['unique'],
Expand All @@ -403,7 +404,7 @@ private function loadTableConstraints(string $tableName, string $returnType): ar

foreach ($tableColumns as $tableColumn) {
if ($tableColumn['pk'] > 0) {
$result[self::PRIMARY_KEY] = new IndexConstraint('', [$tableColumn['name']], true, true);
$result[self::PRIMARY_KEY] = new Index('', [$tableColumn['name']], true, true);
$result[self::INDEXES][] = $result[self::PRIMARY_KEY];
break;
}
Expand Down Expand Up @@ -482,12 +483,12 @@ protected function findViewNames(string $schema = ''): array
private function getJsonColumns(TableSchemaInterface $table): array
{
$result = [];
/** @psalm-var CheckConstraint[] $checks */
/** @psalm-var Check[] $checks */
$checks = $this->getTableChecks((string) $table->getFullName());
$regexp = '/\bjson_valid\(\s*["`\[]?(.+?)["`\]]?\s*\)/i';

foreach ($checks as $check) {
if (preg_match_all($regexp, $check->getExpression(), $matches, PREG_SET_ORDER) > 0) {
if (preg_match_all($regexp, $check->expression, $matches, PREG_SET_ORDER) > 0) {
foreach ($matches as $match) {
$result[] = $match[1];
}
Expand Down
23 changes: 12 additions & 11 deletions tests/Provider/SchemaProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Yiisoft\Db\Schema\Column\IntegerColumn;
use Yiisoft\Db\Schema\Column\JsonColumn;
use Yiisoft\Db\Schema\Column\StringColumn;
use Yiisoft\Db\Tests\Support\Assert;

final class SchemaProvider extends \Yiisoft\Db\Tests\Provider\SchemaProvider
{
Expand Down Expand Up @@ -217,18 +218,18 @@ public static function constraints(): array
{
$constraints = parent::constraints();

$constraints['1: check'][2][0]->columnNames([]);
$constraints['1: check'][2][0]->expression('"C_check" <> \'\'');
$constraints['1: unique'][2][0]->name('sqlite_autoindex_T_constraints_1_2');
$constraints['1: index'][2][0]->name('sqlite_autoindex_T_constraints_1_1');
$constraints['1: index'][2][1]->name('sqlite_autoindex_T_constraints_1_2');
$constraints['2: primary key'][2]->name('');
$constraints['2: unique'][2][0]->name('sqlite_autoindex_T_constraints_2_2');
$constraints['2: index'][2][0]->name('sqlite_autoindex_T_constraints_2_1');
$constraints['2: index'][2][2]->name('sqlite_autoindex_T_constraints_2_2');
$constraints['3: foreign key'][2][0]->name('0');
Assert::setPropertyValue($constraints['1: check'][2][0], 'columnNames', []);
Assert::setPropertyValue($constraints['1: check'][2][0], 'expression', '"C_check" <> \'\'');
Assert::setPropertyValue($constraints['1: unique'][2][0], 'name', 'sqlite_autoindex_T_constraints_1_2');
Assert::setPropertyValue($constraints['1: index'][2][0], 'name', 'sqlite_autoindex_T_constraints_1_1');
Assert::setPropertyValue($constraints['1: index'][2][1], 'name', 'sqlite_autoindex_T_constraints_1_2');
Assert::setPropertyValue($constraints['2: primary key'][2], 'name', '');
Assert::setPropertyValue($constraints['2: unique'][2][0], 'name', 'sqlite_autoindex_T_constraints_2_2');
Assert::setPropertyValue($constraints['2: index'][2][0], 'name', 'sqlite_autoindex_T_constraints_2_1');
Assert::setPropertyValue($constraints['2: index'][2][2], 'name', 'sqlite_autoindex_T_constraints_2_2');
Assert::setPropertyValue($constraints['3: foreign key'][2][0], 'name', '0');
$constraints['3: index'][2] = [];
$constraints['4: unique'][2][0]->name('sqlite_autoindex_T_constraints_4_2');
Assert::setPropertyValue($constraints['4: unique'][2][0], 'name', 'sqlite_autoindex_T_constraints_4_2');

return $constraints;
}
Expand Down
82 changes: 48 additions & 34 deletions tests/SchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
use PHPUnit\Framework\Attributes\DataProviderExternal;
use Throwable;
use Yiisoft\Db\Connection\ConnectionInterface;
use Yiisoft\Db\Constraint\CheckConstraint;
use Yiisoft\Db\Constant\ReferentialAction;
use Yiisoft\Db\Constraint\Check;
use Yiisoft\Db\Constraint\ForeignKey;
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
Expand Down Expand Up @@ -81,7 +83,7 @@ public function testCompositeFk(): void
* @throws InvalidConfigException
* @throws Throwable
*/
public function testForeingKey(): void
public function testForeignKey(): void
{
$db = $this->getConnection();

Expand Down Expand Up @@ -115,10 +117,9 @@ public function testForeingKey(): void
'name' => 'nvarchar(50) null',
],
)->execute();
$foreingKeys = $schema->getTableForeignKeys($tableMaster);
$foreignKeys = $schema->getTableForeignKeys($tableMaster);

$this->assertCount(0, $foreingKeys);
$this->assertSame([], $foreingKeys);
$this->assertSame([], $foreignKeys);

$command->createTable(
$tableRelation,
Expand All @@ -130,14 +131,21 @@ public function testForeingKey(): void
'CONSTRAINT fk_departments FOREIGN KEY (department_id) REFERENCES departments(id) ON DELETE CASCADE',
],
)->execute();
$foreingKeys = $schema->getTableForeignKeys($tableRelation);

$this->assertCount(1, $foreingKeys);
$this->assertSame(['department_id'], $foreingKeys[0]->getColumnNames());
$this->assertSame($tableMaster, $foreingKeys[0]->getForeignTableName());
$this->assertSame(['id'], $foreingKeys[0]->getForeignColumnNames());
$this->assertSame('CASCADE', $foreingKeys[0]->getOnDelete());
$this->assertSame('NO ACTION', $foreingKeys[0]->getOnUpdate());
$foreignKeys = $schema->getTableForeignKeys($tableRelation);
$expectedForeignKeys = [
new ForeignKey(
'0',
['department_id'],
'',
$tableMaster,
['id'],
ReferentialAction::CASCADE,
ReferentialAction::NO_ACTION,
),
];

$this->assertEquals($expectedForeignKeys, $foreignKeys);

$command->createTable(
$tableRelation1,
Expand All @@ -150,19 +158,19 @@ public function testForeingKey(): void
'CONSTRAINT fk_departments FOREIGN KEY (department_id) REFERENCES departments(id) ON DELETE CASCADE',
],
)->execute();
$foreingKeys = $schema->getTableForeignKeys($tableRelation1);

$this->assertCount(2, $foreingKeys);
$this->assertSame(['department_id'], $foreingKeys[0]->getColumnNames());
$this->assertSame($tableMaster, $foreingKeys[0]->getForeignTableName());
$this->assertSame(['id'], $foreingKeys[0]->getForeignColumnNames());
$this->assertSame('CASCADE', $foreingKeys[0]->getOnDelete());
$this->assertSame('NO ACTION', $foreingKeys[0]->getOnUpdate());
$this->assertSame(['student_id'], $foreingKeys[1]->getColumnNames());
$this->assertSame($tableRelation, $foreingKeys[1]->getForeignTableName());
$this->assertSame(['id'], $foreingKeys[1]->getForeignColumnNames());
$this->assertSame('CASCADE', $foreingKeys[1]->getOnDelete());
$this->assertSame('NO ACTION', $foreingKeys[1]->getOnUpdate());

$foreignKeys = $schema->getTableForeignKeys($tableRelation1);
$expectedForeignKeys[] = new ForeignKey(
'1',
['student_id'],
'',
$tableRelation,
['id'],
ReferentialAction::CASCADE,
ReferentialAction::NO_ACTION,
);

$this->assertEquals($expectedForeignKeys, $foreignKeys);
}

public function testMultiForeingKeys(): void
Expand Down Expand Up @@ -204,7 +212,7 @@ public function testGetTableChecks(): void
$tableChecks = $schema->getTableChecks('T_constraints_check');

$this->assertIsArray($tableChecks);
$this->assertContainsOnlyInstancesOf(CheckConstraint::class, $tableChecks);
$this->assertContainsOnlyInstancesOf(Check::class, $tableChecks);
}

/**
Expand Down Expand Up @@ -264,14 +272,20 @@ public function testGetTableForeignKeys(): void
$db = $this->getConnection(true);

$schema = $db->getSchema();
$tableForeingKeys = $schema->getTableForeignKeys('T_constraints_3');

$this->assertCount(1, $tableForeingKeys);
$this->assertSame([ 'C_fk_id_1', 'C_fk_id_2'], $tableForeingKeys[0]->getColumnNames());
$this->assertSame('T_constraints_2', $tableForeingKeys[0]->getForeignTableName());
$this->assertSame(['C_id_1', 'C_id_2'], $tableForeingKeys[0]->getForeignColumnNames());
$this->assertSame('CASCADE', $tableForeingKeys[0]->getOnDelete());
$this->assertSame('CASCADE', $tableForeingKeys[0]->getOnUpdate());
$tableForeignKeys = $schema->getTableForeignKeys('T_constraints_3');

$this->assertEquals(
[new ForeignKey(
'0',
[ 'C_fk_id_1', 'C_fk_id_2'],
'',
'T_constraints_2',
['C_id_1', 'C_id_2'],
ReferentialAction::CASCADE,
ReferentialAction::CASCADE,
)],
$tableForeignKeys
);

$tableTwoForeignKeys = $schema->getTableForeignKeys('foreign_keys_child');
$this->assertCount(2, $tableTwoForeignKeys);
Expand Down