Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 4.3.x up into 5.0.x #6673

Merged
merged 11 commits into from
Dec 26, 2024
21 changes: 21 additions & 0 deletions .github/workflows/website-schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

name: "Website config validation"

on:
pull_request:
branches:
- "*.x"
paths:
- ".doctrine-project.json"
- ".github/workflows/website-schema.yml"
push:
branches:
- "*.x"
paths:
- ".doctrine-project.json"
- ".github/workflows/website-schema.yml"

jobs:
json-validate:
name: "Validate JSON schema"
uses: "doctrine/.github/.github/workflows/[email protected]"
3 changes: 2 additions & 1 deletion UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ The `Sequence::isAutoIncrementsFor()` method has been deprecated.

## Deprecated using invalid database object names

Using the following objects with an empty name is deprecated: `Column`, `View`, `Sequence`, `Identifier`.
Using the following objects with an empty name is deprecated: `Table`, `Column`, `Index`, `View`, `Sequence`,
`Identifier`.

Using the following objects with a qualified name is deprecated: `Column`, `ForeignKeyConstraint`, `Index`, `Schema`,
`UniqueConstraint`. If the object name contains a dot, the name should be quoted.
Expand Down
6 changes: 3 additions & 3 deletions src/Platforms/AbstractMySQLPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,21 +230,21 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio
{
$queryFields = $this->getColumnDeclarationListSQL($columns);

if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
if (! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $definition) {
$queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition);
}
}

// add all indexes
if (isset($options['indexes']) && ! empty($options['indexes'])) {
if (! empty($options['indexes'])) {
foreach ($options['indexes'] as $definition) {
$queryFields .= ', ' . $this->getIndexDeclarationSQL($definition);
}
}

// attach all primary keys
if (isset($options['primary']) && ! empty($options['primary'])) {
if (! empty($options['primary'])) {
$keyColumns = array_unique(array_values($options['primary']));
$queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
}
Expand Down
22 changes: 14 additions & 8 deletions src/Platforms/AbstractPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ private function buildCreateTableSQL(Table $table, bool $createForeignKeys): arr

foreach ($table->getIndexes() as $index) {
if (! $index->isPrimary()) {
$options['indexes'][$index->getQuotedName($this)] = $index;
$options['indexes'][] = $index;

continue;
}
Expand All @@ -819,7 +819,7 @@ private function buildCreateTableSQL(Table $table, bool $createForeignKeys): arr
}

foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
$options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
$options['uniqueConstraints'][] = $uniqueConstraint;
}

if ($createForeignKeys) {
Expand Down Expand Up @@ -961,17 +961,17 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio
{
$columnListSql = $this->getColumnDeclarationListSQL($columns);

if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
if (! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $definition) {
$columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition);
}
}

if (isset($options['primary']) && ! empty($options['primary'])) {
if (! empty($options['primary'])) {
$columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')';
}

if (isset($options['indexes']) && ! empty($options['indexes'])) {
if (! empty($options['indexes'])) {
foreach ($options['indexes'] as $definition) {
$columnListSql .= ', ' . $this->getIndexDeclarationSQL($definition);
}
Expand Down Expand Up @@ -1147,8 +1147,13 @@ public function getCreateSchemaSQL(string $schemaName): string
*/
public function getCreateUniqueConstraintSQL(UniqueConstraint $constraint, string $tableName): string
{
return 'ALTER TABLE ' . $tableName . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this) . ' UNIQUE'
. ' (' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
$sql = 'ALTER TABLE ' . $tableName . ' ADD';

if ($constraint->getName() !== '') {
$sql .= ' CONSTRAINT ' . $constraint->getQuotedName($this);
}

return $sql . ' UNIQUE (' . implode(', ', $constraint->getQuotedColumns($this)) . ')';
}

/**
Expand Down Expand Up @@ -1491,9 +1496,10 @@ public function getUniqueConstraintDeclarationSQL(UniqueConstraint $constraint):
throw new InvalidArgumentException('Incomplete definition. "columns" required.');
}

$chunks = ['CONSTRAINT'];
$chunks = [];

if ($constraint->getName() !== '') {
$chunks[] = 'CONSTRAINT';
$chunks[] = $constraint->getQuotedName($this);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Platforms/PostgreSQLPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio
{
$queryFields = $this->getColumnDeclarationListSQL($columns);

if (isset($options['primary']) && ! empty($options['primary'])) {
if (! empty($options['primary'])) {
$keyColumns = array_unique(array_values($options['primary']));
$queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
}
Expand All @@ -390,7 +390,7 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio

$sql = [$query];

if (isset($options['indexes']) && ! empty($options['indexes'])) {
if (! empty($options['indexes'])) {
foreach ($options['indexes'] as $index) {
$sql[] = $this->getCreateIndexSQL($index, $name);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Platforms/SQLServerPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,13 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio

$columnListSql = $this->getColumnDeclarationListSQL($columns);

if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
if (! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $definition) {
$columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition);
}
}

if (isset($options['primary']) && ! empty($options['primary'])) {
if (! empty($options['primary'])) {
$flags = '';
if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) {
$flags = ' NONCLUSTERED';
Expand All @@ -231,7 +231,7 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio

$sql = [$query];

if (isset($options['indexes']) && ! empty($options['indexes'])) {
if (! empty($options['indexes'])) {
foreach ($options['indexes'] as $index) {
$sql[] = $this->getCreateIndexSQL($index, $name);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Platforms/SQLitePlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio
{
$queryFields = $this->getColumnDeclarationListSQL($columns);

if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) {
if (! empty($options['uniqueConstraints'])) {
foreach ($options['uniqueConstraints'] as $definition) {
$queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition);
}
Expand All @@ -296,13 +296,13 @@ protected function _getCreateTableSQL(string $name, array $columns, array $optio
return $query;
}

if (isset($options['indexes']) && ! empty($options['indexes'])) {
if (! empty($options['indexes'])) {
foreach ($options['indexes'] as $indexDef) {
$query[] = $this->getCreateIndexSQL($indexDef, $name);
}
}

if (isset($options['unique']) && ! empty($options['unique'])) {
if (! empty($options['unique'])) {
foreach ($options['unique'] as $indexDef) {
$query[] = $this->getCreateIndexSQL($indexDef, $name);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Schema/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
use function count;
use function strtolower;

/** @extends AbstractOptionallyNamedObject<UnqualifiedName> */
class Index extends AbstractOptionallyNamedObject
/** @extends AbstractNamedObject<UnqualifiedName> */
class Index extends AbstractNamedObject
{
/**
* Asset identifier instances of the column names the index is associated with.
Expand Down Expand Up @@ -51,7 +51,7 @@ public function __construct(
array $flags = [],
private readonly array $options = [],
) {
parent::__construct($name);
parent::__construct($name ?? '');

$this->_isUnique = $isUnique || $isPrimary;
$this->_isPrimary = $isPrimary;
Expand Down
49 changes: 49 additions & 0 deletions tests/Functional/Schema/ForeignKeyConstraintTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Functional\Schema;

use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;

final class ForeignKeyConstraintTest extends FunctionalTestCase
{
public function testUnnamedForeignKeyConstraint(): void
{
$this->dropTableIfExists('users');
$this->dropTableIfExists('roles');
$this->dropTableIfExists('teams');

$roles = new Table('roles');
$roles->addColumn('id', Types::INTEGER);
$roles->setPrimaryKey(['id']);

$teams = new Table('teams');
$teams->addColumn('id', Types::INTEGER);
$teams->setPrimaryKey(['id']);

$users = new Table('users', [
new Column('id', Type::getType(Types::INTEGER)),
new Column('role_id', Type::getType(Types::INTEGER)),
new Column('team_id', Type::getType(Types::INTEGER)),
], [], [], [
new ForeignKeyConstraint(['role_id'], 'roles', ['id']),
new ForeignKeyConstraint(['team_id'], 'teams', ['id']),
]);
$users->setPrimaryKey(['id']);

$sm = $this->connection->createSchemaManager();
$sm->createTable($roles);
$sm->createTable($teams);
$sm->createTable($users);

$table = $sm->introspectTable('users');

self::assertCount(2, $table->getForeignKeys());
}
}
36 changes: 36 additions & 0 deletions tests/Functional/Schema/UniqueConstraintTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Functional\Schema;

use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\UniqueConstraint;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;

final class UniqueConstraintTest extends FunctionalTestCase
{
public function testUnnamedUniqueConstraint(): void
{
$this->dropTableIfExists('users');

$users = new Table('users', [
new Column('id', Type::getType(Types::INTEGER)),
new Column('username', Type::getType(Types::STRING), ['length' => 32]),
new Column('email', Type::getType(Types::STRING), ['length' => 255]),
], [], [
new UniqueConstraint('', ['username']),
new UniqueConstraint('', ['email']),
], []);

$sm = $this->connection->createSchemaManager();
$sm->createTable($users);

// we want to assert that the two empty names don't clash, but introspection of unique constraints is currently
// not supported. for now, we just assert that the table can be created without exceptions.
$this->expectNotToPerformAssertions();
}
}
28 changes: 17 additions & 11 deletions tests/Schema/IndexTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

namespace Doctrine\DBAL\Tests\Schema;

use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Schema\Exception\InvalidName;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Name\Identifier;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class IndexTest extends TestCase
{
use VerifyDeprecations;

/** @param mixed[] $options */
private function createIndex(bool $unique = false, bool $primary = false, array $options = []): Index
{
Expand Down Expand Up @@ -174,21 +177,24 @@ public function testOptions(): void
self::assertSame(['where' => 'name IS NULL'], $idx2->getOptions());
}

/** @throws Exception */
public function testGetNonNullObjectName(): void
public function testEmptyName(): void
{
$index = new Index('idx_user_id', ['user_id']);
$name = $index->getObjectName();
$this->expectException(InvalidName::class);

self::assertNotNull($name);
self::assertEquals(Identifier::unquoted('idx_user_id'), $name->getIdentifier());
new Index(null, ['user_id']);
}

/** @throws Exception */
public function testGetNullObjectName(): void
public function testQualifiedName(): void
{
$index = new Index(null, ['user_id']);
$this->expectException(InvalidName::class);

new Index('auth.idx_user_id', ['user_id']);
}

public function testGetObjectName(): void
{
$index = new Index('idx_user_id', ['user_id']);

self::assertNull($index->getObjectName());
self::assertEquals(Identifier::unquoted('idx_user_id'), $index->getObjectName()->getIdentifier());
}
}