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

AbstractSchemaManager#_getPortableTableColumnList() cannot process functional indexes defined in MySQL #5306

Open
Ocramius opened this issue Mar 1, 2022 · 6 comments

Comments

@Ocramius
Copy link
Member

Ocramius commented Mar 1, 2022

Bug Report

Q A
Version 3.3.2

Summary

When using functional MySQL indexes, the AbstractSchemaManager crashes when instantiating a new Index for which the columns could not be identified.

How to reproduce

Given following DDL:

CREATE TABLE product (
  id BINARY(16) NOT NULL,
  -- ...
  custom_fields JSON NOT NULL,
  PRIMARY KEY(id)
);

CREATE INDEX IDX_SOME_SUBFIELD
    ON `product`
    (
        (JSON_VALUE(custom_fields, '$.mySubfield' RETURNING CHAR(255)))
    );

Running schema introspection tools lea

$ bin/console doctrine:schema:update --dump-sql  -vvv
In Index.php line 83:
                                                                                                                                               
  [TypeError]                                                                                                                                  
  Doctrine\DBAL\Schema\Index::_addColumn(): Argument #1 ($column) must be of type string, null given, called in /srv/share/vendor/doctrine/db  
  al/src/Schema/Index.php on line 72                                                                                                           
                                                                                                                                               

Exception trace:
  at /srv/share/vendor/doctrine/dbal/src/Schema/Index.php:83
 Doctrine\DBAL\Schema\Index->_addColumn() at /srv/share/vendor/doctrine/dbal/src/Schema/Index.php:72
 Doctrine\DBAL\Schema\Index->__construct() at /srv/share/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php:1072
 Doctrine\DBAL\Schema\AbstractSchemaManager->_getPortableTableIndexesList() at /srv/share/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php:104
 Doctrine\DBAL\Schema\MySQLSchemaManager->_getPortableTableIndexesList() at /srv/share/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php:228
 Doctrine\DBAL\Schema\AbstractSchemaManager->listTableIndexes() at /srv/share/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php:328
 Doctrine\DBAL\Schema\AbstractSchemaManager->listTableDetails() at /srv/share/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php:336
 Doctrine\DBAL\Schema\MySQLSchemaManager->listTableDetails() at /srv/share/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php:306
 Doctrine\DBAL\Schema\AbstractSchemaManager->listTables() at /srv/share/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php:1229
 Doctrine\DBAL\Schema\AbstractSchemaManager->createSchema() at /srv/share/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php:966
 Doctrine\ORM\Tools\SchemaTool->createSchemaForComparison() at /srv/share/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php:937
 Doctrine\ORM\Tools\SchemaTool->getUpdateSchemaSql() at /srv/share/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php:79
 Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand->executeSchemaCommand() at /srv/share/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php:46
 Doctrine\ORM\Tools\Console\Command\SchemaTool\AbstractCommand->execute() at /srv/share/vendor/doctrine/doctrine-bundle/Command/Proxy/UpdateSchemaDoctrineCommand.php:40
 Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand->execute() at /srv/share/vendor/symfony/console/Command/Command.php:291
 Symfony\Component\Console\Command\Command->run() at /srv/share/vendor/symfony/console/Application.php:1007
 Symfony\Component\Console\Application->doRunCommand() at /srv/share/vendor/symfony/framework-bundle/Console/Application.php:96
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /srv/share/vendor/symfony/console/Application.php:299
 Symfony\Component\Console\Application->doRun() at /srv/share/vendor/symfony/framework-bundle/Console/Application.php:82
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /srv/share/vendor/symfony/console/Application.php:171
 Symfony\Component\Console\Application->run() at /srv/share/bin/console:29

The trace is a bit noisy because of this happening in a symfony application, but the last frames are clear.

This happens here:

$index = new Index(
$data['name'],
$data['columns'],
$data['unique'],
$data['primary'],
$data['flags'],
$data['options']
);

At this location, $data['columns'][0] will be null for the index defined above

Current behaviour

See above crash - a hard crash is probably to be avoided.

Expected behaviour

Perhaps letting it silently ignore the index? 🤔

In my application, I will likely come up with a workaround that skips this specific index through an event subscriber attached to \Doctrine\DBAL\Events::onSchemaIndexDefinition, but it is only a local workaround.

@morozov
Copy link
Member

morozov commented Mar 1, 2022

At a high level, this is similar to #4470: the DBAL crashes upon encountering a database object that it doesn't understand, while it has a quite limited understanding of the supported platforms.

From the design standpoint, in both of these cases, the DBAL might fall back to using some generic types for such objects (i.e. columns and indices). It should operate lower-level raw definitions that won't likely allow comparison but will retain the original properties of the objects.

@YosraHamza
Copy link

Any updates here? and when it is expected to be fixed?
I'm using a Laravel admin panel (Backpack v5) and they are using the package in the core operations

@Ocramius
Copy link
Member Author

@YosraHamza this is open source: it is fixed when YOU fix it :P

@YosraHamza
Copy link

@Ocramius I agree with you. I saw the open discussion and related issues so I thought it might be resolved soon. If not, I will give it a try for sure.

@lukasm91
Copy link

Really not saying this is the proper solution (open to suggestion, but I am looking at this project the first time, so it is likely just some workaround, I am neither a PHP expert nor into databases).

https://github.com/lukasm91/dbal/tree/fix_invalid_columns

This is just skipping indices that have an index with unknown columns. I ran into this with a typo3 application with a sqlite backend.

@ES-Six
Copy link

ES-Six commented Jan 20, 2023

Just encountered this exact same issue.

Here is how to reproduce :

Applyed the following migration by running php bin/console doctrine:migrations:migrate :

<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20230120105840 extends AbstractMigration
{
    public function getDescription(): string
    {
        return '';
    }

    public function up(Schema $schema): void
    {
        // this up() migration is auto-generated, please modify it to your needs
        $this->addSql("ALTER TABLE company ADD INDEX idx_siret ((  cast(regexp_replace(cast(`corporate_identifier` as char charset utf8mb4),_utf8mb4'[^0-9]+',_utf8mb4'') as unsigned)   ))");
    }

    public function down(Schema $schema): void
    {
        // this down() migration is auto-generated, please modify it to your needs
        $this->addSql('DROP INDEX idx_siret ON company');
    }
}

Then I ran php bin/console doctrine:migrations:diff.

And this error happened :
Doctrine\DBAL\Schema\Index::_addColumn(): Argument #1 ($column) must be of type string, null given, called in /var/ www/vendor/doctrine/dbal/src/Schema/Index.php on line 72

This error occured because the _addColumn function take a string parameter but mysql functionnal indexes has no column at all, so null is passed to this function and dbal crash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants