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

The changes from #3392 break indexes with a fixed length #3409

Closed
leofeyer opened this issue Dec 14, 2018 · 15 comments
Closed

The changes from #3392 break indexes with a fixed length #3409

leofeyer opened this issue Dec 14, 2018 · 15 comments

Comments

@leofeyer
Copy link
Contributor

BC Break Report

Q A
BC Break yes
Version 2.9.1

Summary

The changes from #3392 break indexes with a fixed length.

Previous behaviour

$table = new Table('test');
$table->addColumn('path', 'text');
$table->addIndex(['path' => 'path(768)'], 'path', [], ['lengths' => [768]]);

This added the following index:

Current behavior

Doctrine\DBAL\Schema\SchemaException:
There is no column with name 'path(768)' on table 'test'.

The exception seems to occur due to the removal of the following lines in the Table::_createIndex() method:

        if (is_numeric($columnName) && is_string($indexColOptions)) {
            $columnName = $indexColOptions;
        }
@morozov
Copy link
Member

morozov commented Dec 14, 2018

@leofeyer could you clarify why you're using this syntax for fixed length indices? AFAIK, the support for such indices was only added in v2.9.0, and the syntax is expected to look like the following:

$table = new Table('test_partial_column_index');
$table->addColumn('long_column', 'string', ['length' => 40]);
$table->addColumn('standard_column', 'integer');
$table->addIndex(['long_column'], 'partial_long_column_idx', [], ['lengths' => [4]]);
$table->addIndex(['standard_column', 'long_column'], 'standard_and_partial_idx', [], ['lengths' => [null, 2]]);

The first argument contains column names, the last contains the lenghts which is sufficient to define the index.

@leofeyer
Copy link
Contributor Author

We have been using it since Doctrine DBAL 2.6, however, you are right about the lengths option, which we have only added very recently. Before that the code looked like this:

$table = new Table('test');
$table->addColumn('path', 'text');
$table->addIndex(['path' => 'path(768)'], 'path');

@morozov
Copy link
Member

morozov commented Dec 15, 2018

@leofeyer it doesn't look like valid usage to me. The first argument of Table::addIndex() has always been array $columnNames. It accepts a list of names, not a list of SQL expressions like you're trying to pass.

@morozov morozov self-assigned this Dec 16, 2018
@morozov morozov closed this as completed Dec 16, 2018
@leofeyer
Copy link
Contributor Author

leofeyer commented Dec 16, 2018

it doesn't look like valid usage to me

Ok, then please tell me how to achieve the following data structure:

Because this is exactly what $connection->getSchemaManager()->createSchema() returns and also what phpMyAdmin creates:

And it cannot be created like this either:

$table->addIndex(['path(768)'], 'path', [], ['lengths' => [768]]);

So how do I accomplish it?

@morozov
Copy link
Member

morozov commented Dec 16, 2018

@leofeyer does the approach covered in #3409 (comment) work for you?

@leofeyer
Copy link
Contributor Author

No, it does not, because it does not set the correct column name. The column name needs to be path(768) to match the return value of $schemaManager->createSchema().

Create schema from database

CREATE TABLE test (path text NULL);
CREATE INDEX path ON test (path(768));
$connection = $this->get('database_connection'); // Symfony DI container
dump($connection->getSchemaManager()->createSchema()->getTable('test'));

Create schema manually

$table = new Table('test');
$table->addColumn('path', 'text');
$table->addIndex(['path'], 'path', [], ['lengths' => [768]]);
dump($table);

And because of this difference, Schema::getMigrateToSql() no longer handles the index correctly.

Another possible solution

Maybe $table->addIndex(['path'], 'path', [], ['lengths' => [768]]); should just add the index length to the column name? Then the manual schema would match the created schema again.

@morozov
Copy link
Member

morozov commented Dec 17, 2018

The column name needs to be path(768) to match the return value of $schemaManager->createSchema().

It should be the opposite. path(768) is not a column name. It's an SQL expression representing an index column (by name) and its length.

When a table is created from its definition by the DBAL, the SQL looks like:

CREATE TABLE index_length (
    path TEXT NULL,
    INDEX IDX_18E36143B548B0F (path(768))
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB

and the introspection works correctly (the following test passes):

public function testIndexLength()
{
    $table = new Table('index_length');
    $table->addColumn('path', 'text');
    $table->addIndex(['path'], null, [], ['lengths' => [768]]);

    $this->schemaManager->dropAndCreateTable($table);

    $indexes = $this->schemaManager->listTableIndexes('index_length');
    self::assertCount(1, $indexes);

    $index = array_shift($indexes);
    self::assertEquals(['path'], $index->getColumns());
    self::assertEquals([768], $index->getOption('lengths'));
}

It also passes with your syntax:

public function testIndexLength()
{
    $this->schemaManager->tryMethod('dropTable', 'index_length');
    $this->connection->exec('CREATE TABLE index_length (path text NULL)');
    $this->connection->exec('CREATE INDEX path ON index_length (path(768))');

    $indexes = $this->schemaManager->listTableIndexes('index_length');
    self::assertCount(1, $indexes);

    $index = array_shift($indexes);
    self::assertEquals(['path'], $index->getColumns());
    self::assertEquals([768], $index->getOption('lengths'));
}

In both cases, the index is introspected correctly as having one column path and its indexed length 768. Could you please provide a failing case similar to the above? Right now I don't see where the problem is.

@leofeyer
Copy link
Contributor Author

leofeyer commented Dec 17, 2018

It should be the opposite. path(768) is not a column name. It's an SQL expression representing an index column (by name) and its length.

Either way, Schema::getMigrateToSql() is broken now in version 2.9.1 due to the column identifier being different from the one returned by $schemaManager->createSchema().

@beberlei @Ocramius Maybe you can take a look at this issue as well? I have already provided code samples and screenshots in #3409 (comment) and I don't know how to better explain it. It seems we are stuck here. 🙈

@Ocramius
Copy link
Member

@leofeyer I haven't got the slightest clue: @morozov already provided a test scenario, it is up to you to provide a failing test case now.

@leofeyer
Copy link
Contributor Author

Ok, will do tomorrow.

@leofeyer
Copy link
Contributor Author

So here is the failing test:

public function testUpdatesIndexLength()
{
    $this->connection->exec('CREATE TABLE index_length (path text NULL)');
    $this->connection->exec('CREATE INDEX path ON index_length (path(333))');

    $table = new Table('index_length');
    $table->addColumn('path', 'text', ['notnull' => false]);
    $table->addIndex(['path'], 'path', [], ['lengths' => [768]]);

    $fromSchema = $this->connection->getSchemaManager()->createSchema();
    $toSchema = new Schema([$table]);

    $diff = $fromSchema->getMigrateToSql($toSchema, $this->connection->getDatabasePlatform());

    self::assertContains('ALTER TABLE index_length DROP INDEX path, ADD INDEX path (path(768))', $diff);
}

@Ocramius
Copy link
Member

Can you please send that in a PR? That would basically restart the issue from a fresh/solid ground 👍

@leofeyer
Copy link
Contributor Author

Sure, if you tell me in which class the test belongs.

@morozov
Copy link
Member

morozov commented Dec 18, 2018

@leofeyer thank you for the test. I'll figure out first what exactly is broken and then we'll see where the test belongs.

@github-actions
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 31, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants